home *** CD-ROM | disk | FTP | other *** search
/ Aminet 35 / Aminet 35 (2000)(Schatztruhe)[!][Feb 2000].iso / Aminet / game / shoot / ADescentSrc.lha / descent / main / ai.c next >
C/C++ Source or Header  |  1998-08-08  |  129KB  |  3,620 lines

  1. /*
  2. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  3. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  4. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  5. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  6. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  7. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  8. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  9. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  10. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
  11. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  12. */
  13. /*
  14.  * $Source: /usr/CVS/descent/main/ai.c,v $
  15.  * $Revision: 1.3 $
  16.  * $Author: nobody $
  17.  * $Date: 1998/08/08 15:42:04 $
  18.  *
  19.  * Autonomous Individual movement.
  20.  *
  21.  * $Log: ai.c,v $
  22.  * Revision 1.3  1998/08/08 15:42:04  nobody
  23.  * Activated the Editior
  24.  *
  25.  * Revision 1.2  1998/04/09 16:15:07  tfrieden
  26.  * Removed old log messages
  27.  *
  28.  * Revision 1.1.1.1  1998/03/03 15:12:10  nobody
  29.  * reimport after crash from backup
  30.  *
  31.  * Revision 1.1.1.1  1998/02/13  20:20:37  hfrieden
  32.  * Initial Import
  33.  */
  34.  
  35. #pragma off (unreferenced)
  36. static char rcsid[] = "$Id: ai.c,v 1.3 1998/08/08 15:42:04 nobody Exp $";
  37. #pragma on (unreferenced)
  38.  
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include <bsd/bsd.h>
  42.  
  43. #include "inferno.h"
  44. #include "game.h"
  45. #include "mono.h"
  46. #include "3d.h"
  47.  
  48. #include "object.h"
  49. #include "render.h"
  50. #include "error.h"
  51. #include "ai.h"
  52. #include "laser.h"
  53. #include "fvi.h"
  54. #include "polyobj.h"
  55. #include "bm.h"
  56. #include "weapon.h"
  57. #include "physics.h"
  58. #include "collide.h"
  59. #include "fuelcen.h"
  60. #include "player.h"
  61. #include "wall.h"
  62. #include "vclip.h"
  63. #include "digi.h"
  64. #include "fireball.h"
  65. #include "morph.h"
  66. #include "effects.h"
  67. #include "timer.h"
  68. #include "sounds.h"
  69. #include "cntrlcen.h"
  70. #include "multibot.h"
  71. #include "multi.h"
  72. #include "network.h"
  73. #include "gameseq.h"
  74. #include "key.h"
  75. #include "powerup.h"
  76. #include "gauges.h"
  77. #include "text.h"
  78.  
  79. #ifdef EDITOR
  80. #include "editor/editor.h"
  81. #endif
  82.  
  83. #ifndef NDEBUG
  84. #include "string.h"
  85. #include <time.h>
  86. #endif
  87.  
  88. #define JOHN_CHEATS_SIZE_1  6
  89. #define JOHN_CHEATS_SIZE_2  6
  90. #define JOHN_CHEATS_SIZE_3  6
  91.  
  92. ubyte   john_cheats_1[JOHN_CHEATS_SIZE_1] = {   KEY_P ^ 0x00 ^ 0x34, 
  93.                                                             KEY_O ^ 0x10 ^ 0x34, 
  94.                                                             KEY_B ^ 0x20 ^ 0x34, 
  95.                                                             KEY_O ^ 0x30 ^ 0x34, 
  96.                                                             KEY_Y ^ 0x40 ^ 0x34, 
  97.                                                             KEY_S ^ 0x50 ^ 0x34 };
  98.  
  99. #define PARALLAX    0       //  If !0, then special debugging info for Parallax eyes only enabled.
  100.  
  101. #define MIN_D 0x100
  102.  
  103. int Flinch_scale = 4;
  104. int john_cheats_index_1;        //  POBOYS      detonate reactor
  105. int Attack_scale = 24;
  106. #define ANIM_RATE       (F1_0/16)
  107. #define DELTA_ANG_SCALE 16
  108.  
  109. byte Mike_to_matt_xlate[] = {AS_REST, AS_REST, AS_ALERT, AS_ALERT, AS_FLINCH, AS_FIRE, AS_RECOIL, AS_REST};
  110. int john_cheats_index_2;        //  PORGYS      high speed weapon firing
  111.  
  112. // int  No_ai_flag=0;
  113.  
  114. #define OVERALL_AGITATION_MAX   100
  115.  
  116. #define     MAX_AI_CLOAK_INFO   8   //  Must be a power of 2!
  117.  
  118. typedef struct {
  119.     fix         last_time;
  120.     vms_vector  last_position;
  121. } ai_cloak_info;
  122.  
  123. #define BOSS_CLOAK_DURATION (F1_0*7)
  124. #define BOSS_DEATH_DURATION (F1_0*6)
  125. #define BOSS_DEATH_SOUND_DURATION   0x2ae14     //  2.68 seconds
  126.  
  127. //  Amount of time since the current robot was last processed for things such as movement.
  128. //  It is not valid to use FrameTime because robots do not get moved every frame.
  129. //fix   AI_proc_time;
  130.  
  131. int Num_boss_teleport_segs;
  132. short   Boss_teleport_segs[MAX_BOSS_TELEPORT_SEGS];
  133. #ifndef SHAREWARE
  134. int Num_boss_gate_segs;
  135. short   Boss_gate_segs[MAX_BOSS_TELEPORT_SEGS];
  136. #endif
  137.  
  138. int john_cheats_index_3;        //  LUNACY      lunacy (insane behavior, rookie firing)
  139.  
  140. //  ---------- John: These variables must be saved as part of gamesave. ----------
  141. int             Ai_initialized = 0;
  142. int             Overall_agitation;
  143. ai_local            Ai_local_info[MAX_OBJECTS];
  144. point_seg       Point_segs[MAX_POINT_SEGS];
  145. point_seg       *Point_segs_free_ptr = Point_segs;
  146. ai_cloak_info   Ai_cloak_info[MAX_AI_CLOAK_INFO];
  147. fix             Boss_cloak_start_time = 0;
  148. fix             Boss_cloak_end_time = 0;
  149. fix             Last_teleport_time = 0;
  150. fix             Boss_teleport_interval = F1_0*8;
  151. fix             Boss_cloak_interval = F1_0*10;                  //  Time between cloaks
  152. fix             Boss_cloak_duration = BOSS_CLOAK_DURATION;
  153. fix             Last_gate_time = 0;
  154. fix             Gate_interval = F1_0*6;
  155. fix             Boss_dying_start_time;
  156. int             Boss_dying, Boss_dying_sound_playing, Boss_hit_this_frame;
  157. int             Boss_been_hit=0;
  158.  
  159.  
  160. //  ---------- John: End of variables which must be saved as part of gamesave. ----------
  161.  
  162. int john_cheats_index_4;        //  PLETCHnnn   paint robots
  163. int             ai_evaded=0;
  164.  
  165. #ifndef SHAREWARE
  166. //  0   mech
  167. //  1   green claw
  168. //  2   spider
  169. //  3   josh
  170. //  4   violet
  171. //  5   cloak vulcan
  172. //  6   cloak mech
  173. //  7   brain
  174. //  8   onearm
  175. //  9   plasma
  176. //  10  toaster
  177. //  11  bird
  178. //  12  missile bird
  179. //  13  polyhedron
  180. //  14  baby spider
  181. //  15  mini boss
  182. //  16  super mech
  183. //  17  shareware boss
  184. //  18  cloak-green ; note, gating in this guy benefits player, cloak objects
  185. //  19  vulcan
  186. //  20  toad
  187. //  21  4-claw
  188. //  22  quad-laser
  189. // 23 super boss
  190.  
  191. // byte Super_boss_gate_list[] = {0, 1, 2, 9, 11, 16, 18, 19, 21, 22, 0, 9, 9, 16, 16, 18, 19, 19, 22, 22};
  192. byte    Super_boss_gate_list[] = {0, 1, 8, 9, 10, 11, 12, 15, 16, 18, 19, 20, 22, 0, 8, 11, 19, 20, 8, 20, 8};
  193. #define MAX_GATE_INDEX  ( sizeof(Super_boss_gate_list) / sizeof(Super_boss_gate_list[0]) )
  194. #endif
  195.  
  196. int Ai_info_enabled=0;
  197. int Robot_firing_enabled = 1;
  198.  
  199. extern  int Ugly_robot_cheat, Ugly_robot_texture, Laser_rapid_fire;
  200. extern  byte    Enable_john_cheat_1, Enable_john_cheat_2, Enable_john_cheat_3, Enable_john_cheat_4;
  201.  
  202. ubyte   john_cheats_3[2*JOHN_CHEATS_SIZE_3+1] = { KEY_Y ^ 0x67, 
  203.                                                                 KEY_E ^ 0x66, 
  204.                                                                 KEY_C ^ 0x65, 
  205.                                                                 KEY_A ^ 0x64, 
  206.                                                                 KEY_N ^ 0x63, 
  207.                                                                 KEY_U ^ 0x62, 
  208.                                                                 KEY_L ^ 0x61 };
  209.  
  210.  
  211. #define MAX_AWARENESS_EVENTS    64
  212. typedef struct awareness_event {
  213.     short       segnum;             // segment the event occurred in
  214.     short           type;                   // type of event, defines behavior
  215.     vms_vector  pos;                    // absolute 3 space location of event
  216. } awareness_event;
  217.  
  218.  
  219. //  These globals are set by a call to find_vector_intersection, which is a slow routine,
  220. //  so we don't want to call it again (for this object) unless we have to.
  221. vms_vector  Hit_pos;
  222. int         Hit_type, Hit_seg;
  223. fvi_info        Hit_data;
  224.  
  225. int                 Num_awareness_events = 0;
  226. awareness_event Awareness_events[MAX_AWARENESS_EVENTS];
  227.  
  228. vms_vector      Believed_player_pos;
  229.  
  230. #define AIS_MAX 8
  231. #define AIE_MAX 4
  232.  
  233. //--unused-- int    Processed_this_frame, LastFrameCount;
  234. #ifndef NDEBUG
  235. //  Index into this array with ailp->mode
  236. char    mode_text[8][9] = {
  237.     "STILL   ",
  238.     "WANDER  ",
  239.     "FOL_PATH",
  240.     "CHASE_OB",
  241.     "RUN_FROM",
  242.     "HIDE    ",
  243.     "FOL_PAT2",
  244.     "OPENDOR2"
  245. };
  246.  
  247. //  Index into this array with aip->behavior
  248. char    behavior_text[6][9] = {
  249.     "STILL   ",
  250.     "NORMAL  ",
  251.     "HIDE    ",
  252.     "RUN_FROM",
  253.     "FOLPATH ",
  254.     "STATION "
  255. };
  256.  
  257. //  Index into this array with aip->GOAL_STATE or aip->CURRENT_STATE
  258. char    state_text[8][5] = {
  259.     "NONE",
  260.     "REST",
  261.     "SRCH",
  262.     "LOCK",
  263.     "FLIN",
  264.     "FIRE",
  265.     "RECO",
  266.     "ERR_",
  267. };
  268.  
  269.  
  270. int Ai_animation_test=0;
  271. #endif
  272.  
  273. // Current state indicates where the robot current is, or has just done.
  274. //  Transition table between states for an AI object.
  275. //   First dimension is trigger event.
  276. //  Second dimension is current state.
  277. //   Third dimension is goal state.
  278. //  Result is new goal state.
  279. //  ERR_ means something impossible has happened.
  280. byte Ai_transition_table[AI_MAX_EVENT][AI_MAX_STATE][AI_MAX_STATE] = {
  281.     {
  282.     //  Event = AIE_FIRE, a nearby object fired
  283.     //  none            rest            srch            lock            flin            fire            reco                // CURRENT is rows, GOAL is columns
  284.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_RECO},      //  none
  285.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_RECO},      //  rest
  286.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_RECO},      //  search
  287.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_RECO},      //  lock
  288.     {   AIS_ERR_,   AIS_REST,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FIRE,   AIS_RECO},      //  flinch
  289.     {   AIS_ERR_,   AIS_FIRE,   AIS_FIRE,   AIS_FIRE,   AIS_FLIN,   AIS_FIRE,   AIS_RECO},      //  fire
  290.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_FIRE}       //  recoil
  291.     },
  292.  
  293.     //  Event = AIE_HITT, a nearby object was hit (or a wall was hit)
  294.     {
  295.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_RECO},
  296.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_RECO},
  297.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_RECO},
  298.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_RECO},
  299.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FLIN},
  300.     {   AIS_ERR_,   AIS_REST,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FIRE,   AIS_RECO},
  301.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_FIRE}
  302.     },
  303.  
  304.     //  Event = AIE_COLL, player collided with robot
  305.     {
  306.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_RECO},
  307.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_RECO},
  308.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_RECO},
  309.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_RECO},
  310.     {   AIS_ERR_,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_LOCK,   AIS_FLIN,   AIS_FLIN},
  311.     {   AIS_ERR_,   AIS_REST,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FIRE,   AIS_RECO},
  312.     {   AIS_ERR_,   AIS_LOCK,   AIS_LOCK,   AIS_LOCK,   AIS_FLIN,   AIS_FIRE,   AIS_FIRE}
  313.     },
  314.  
  315.     //  Event = AIE_HURT, player hurt robot (by firing at and hitting it)
  316.     //  Note, this doesn't necessarily mean the robot JUST got hit, only that that is the most recent thing that happened.
  317.     {
  318.     {   AIS_ERR_,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN},
  319.     {   AIS_ERR_,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN},
  320.     {   AIS_ERR_,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN},
  321.     {   AIS_ERR_,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN},
  322.     {   AIS_ERR_,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN},
  323.     {   AIS_ERR_,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN},
  324.     {   AIS_ERR_,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN,   AIS_FLIN}
  325.     }
  326. };
  327.  
  328. ubyte   john_cheats_2[2*JOHN_CHEATS_SIZE_2] = {     KEY_P ^ 0x00 ^ 0x43, 0x66, 
  329.                                                                 KEY_O ^ 0x10 ^ 0x43, 0x11, 
  330.                                                                 KEY_R ^ 0x20 ^ 0x43, 0x8, 
  331.                                                                 KEY_G ^ 0x30 ^ 0x43, 0x2, 
  332.                                                                 KEY_Y ^ 0x40 ^ 0x43, 0x0, 
  333.                                                                 KEY_S ^ 0x50 ^ 0x43 };
  334.  
  335. // ---------------------------------------------------------
  336. //  On entry, N_robot_types had darn sure better be set.
  337. //  Mallocs N_robot_types robot_info structs into global Robot_info.
  338. void init_ai_system(void)
  339. {
  340. #if 0
  341.     int i;
  342.  
  343.     mprintf((0, "Trying to malloc %i bytes for Robot_info.\n", N_robot_types * sizeof(*Robot_info)));
  344.     Robot_info = (robot_info *) malloc( N_robot_types * sizeof(*Robot_info) );
  345.     mprintf((0, "Robot_info = %i\n", Robot_info));
  346.  
  347.     for (i=0; i<N_robot_types; i++) {
  348.         Robot_info[i].field_of_view = F1_0/2;
  349.         Robot_info[i].firing_wait = F1_0;
  350.         Robot_info[i].turn_time = F1_0*2;
  351.         Robot_info[i].fire_power = F1_0;
  352.         Robot_info[i].shield = F1_0/2;
  353.         Robot_info[i].max_speed = F1_0*10;
  354.         Robot_info[i].always_0xabcd = 0xabcd;
  355.     }
  356. #endif
  357.  
  358. }
  359.  
  360. void john_cheat_func_1(int key)
  361. {
  362.     if (!Cheats_enabled)
  363.         return;
  364.  
  365.     if (key == (john_cheats_1[john_cheats_index_1] ^ (john_cheats_index_1 << 4) ^ 0x34)) {
  366.         john_cheats_index_1++;
  367.         if (john_cheats_index_1 == JOHN_CHEATS_SIZE_1)  {
  368.             do_controlcen_destroyed_stuff(NULL);
  369.             john_cheats_index_1 = 0;
  370.             digi_play_sample( SOUND_CHEATER, F1_0);
  371.         }
  372.     } else
  373.         john_cheats_index_1 = 0;
  374. }
  375.  
  376. // ---------------------------------------------------------------------------------------------------------------------
  377. //  Given a behavior, set initial mode.
  378. int ai_behavior_to_mode(int behavior)
  379. {
  380.     switch (behavior) {
  381.         case AIB_STILL:         return AIM_STILL;
  382.         case AIB_NORMAL:            return AIM_CHASE_OBJECT;
  383.         case AIB_HIDE:              return AIM_HIDE;
  384.         case AIB_RUN_FROM:      return AIM_RUN_FROM_OBJECT;
  385.         case AIB_FOLLOW_PATH:   return AIM_FOLLOW_PATH;
  386.         case AIB_STATION:           return AIM_STILL;
  387.         default:    Int3(); //  Contact Mike: Error, illegal behavior type
  388.     }
  389.  
  390.     return AIM_STILL;
  391. }
  392.  
  393. // ---------------------------------------------------------------------------------------------------------------------
  394. //  Call every time the player starts a new ship.
  395. void ai_init_boss_for_ship(void)
  396. {
  397.     Boss_been_hit = 0;
  398. }
  399.  
  400. // ---------------------------------------------------------------------------------------------------------------------
  401. //  initial_mode == -1 means leave mode unchanged.
  402. void init_ai_object(int objnum, int behavior, int hide_segment)
  403. {
  404.     object  *objp = &Objects[objnum];
  405.     ai_static   *aip = &objp->ctype.ai_info;
  406.     ai_local        *ailp = &Ai_local_info[objnum];
  407.  
  408. #ifdef DEST_SAT
  409.         if (!(Game_mode & GM_MULTI) && Robot_info[objp->id].boss_flag) {
  410.             mprintf((0, "Current_level_num = %i, Last_level = %i\n", Current_level_num, Last_level));
  411.             if (Current_level_num != Last_level) {
  412.                 mprintf((0, "Removing boss, object num = %i\n", objnum));
  413.                 objp->id = 0;
  414.                 objp->flags |= OF_SHOULD_BE_DEAD;
  415.             }
  416.         }
  417. #endif
  418.  
  419.     if (behavior == 0) {
  420.         // mprintf((0, "Behavior of 0 for object #%i, bashing to AIB_NORMAL.\n", objnum));
  421.         behavior = AIB_NORMAL;
  422.         objp->ctype.ai_info.behavior = behavior;
  423.     }
  424.     // mprintf((0, "Initializing object #%i\n", objnum));
  425.  
  426.     //  mode is now set from the Robot dialog, so this should get overwritten.
  427.     ailp->mode = AIM_STILL;
  428.  
  429.     ailp->previous_visibility = 0;
  430.  
  431.     if (behavior != -1) {
  432.         aip->behavior = behavior;
  433.         ailp->mode = ai_behavior_to_mode(aip->behavior);
  434.     } else if (!((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR))) {
  435.         mprintf((0, "[obj %i -> normal] ", objnum));
  436.         aip->behavior = AIB_NORMAL;
  437.     }
  438.  
  439.     // This is astonishingly stupid!  This routine gets called by matcens! KILL KILL KILL!!! Point_segs_free_ptr = Point_segs;
  440.  
  441.     vm_vec_zero(&objp->mtype.phys_info.velocity);
  442.     // -- ailp->wait_time = F1_0*5;
  443.     ailp->player_awareness_time = 0;
  444.     ailp->player_awareness_type = 0;
  445.     aip->GOAL_STATE = AIS_SRCH;
  446.     aip->CURRENT_STATE = AIS_REST;
  447.     ailp->time_player_seen = GameTime;
  448.     ailp->next_misc_sound_time = GameTime;
  449.     ailp->time_player_sound_attacked = GameTime;
  450.  
  451.     if ((behavior == AIB_HIDE) || (behavior == AIB_FOLLOW_PATH) || (behavior == AIB_STATION) || (behavior == AIB_RUN_FROM)) {
  452.         aip->hide_segment = hide_segment;
  453.         ailp->goal_segment = hide_segment;
  454.         aip->hide_index = -1;           // This means the path has not yet been created.
  455.         aip->cur_path_index = 0;
  456.     }
  457.  
  458.     aip->SKIP_AI_COUNT = 0;
  459.  
  460.     if (Robot_info[objp->id].cloak_type == RI_CLOAKED_ALWAYS)
  461.         aip->CLOAKED = 1;
  462.     else
  463.         aip->CLOAKED = 0;
  464.  
  465.     objp->mtype.phys_info.flags |= (PF_BOUNCE | PF_TURNROLL);
  466.     
  467.     aip->REMOTE_OWNER = -1;
  468. }
  469.  
  470. void john_cheat_func_2(int key)
  471. {
  472.     if (!Cheats_enabled)
  473.         return;
  474.  
  475.     if (key == (john_cheats_2[2*john_cheats_index_2] ^ (john_cheats_index_2 << 4) ^ 0x43)) {
  476.         john_cheats_index_2++;
  477.         if (john_cheats_index_2 == JOHN_CHEATS_SIZE_2) {
  478.             Laser_rapid_fire = 0xBADA55;
  479.             do_megawow_powerup(200);
  480.             john_cheats_index_2 = 0;
  481.             digi_play_sample( SOUND_CHEATER, F1_0);
  482.         }
  483.     } else
  484.         john_cheats_index_2 = 0;
  485. }
  486.  
  487. // ---------------------------------------------------------------------------------------------------------------------
  488. void init_ai_objects(void)
  489. {
  490.     int i;
  491.  
  492.     Point_segs_free_ptr = Point_segs;
  493.  
  494.     for (i=0; i<MAX_OBJECTS; i++) {
  495.         object *objp = &Objects[i];
  496.  
  497.         if (objp->control_type == CT_AI)
  498.             init_ai_object(i, objp->ctype.ai_info.behavior, objp->ctype.ai_info.hide_segment);
  499.     }
  500.  
  501.     init_boss_segments(Boss_teleport_segs, &Num_boss_teleport_segs, 1);
  502.  
  503.     #ifndef SHAREWARE
  504.         init_boss_segments(Boss_gate_segs, &Num_boss_gate_segs, 0);
  505.     #endif
  506.  
  507.     Boss_dying_sound_playing = 0;
  508.     Boss_dying = 0;
  509.     Boss_been_hit = 0;
  510.     #ifndef SHAREWARE
  511.     Gate_interval = F1_0*5 - Difficulty_level*F1_0/2;
  512.     #endif
  513.  
  514.     Ai_initialized = 1;
  515. }
  516.  
  517. int Lunacy = 0;
  518. int Diff_save = 1;
  519.  
  520. fix Firing_wait_copy[MAX_ROBOT_TYPES];
  521. byte    Rapidfire_count_copy[MAX_ROBOT_TYPES];
  522.  
  523. void do_lunacy_on(void)
  524. {
  525.     int i;
  526.  
  527.     if ( !Lunacy )  {
  528.         Lunacy = 1;
  529.         Diff_save = Difficulty_level;
  530.         Difficulty_level = NDL-1;
  531.  
  532.         for (i=0; i<MAX_ROBOT_TYPES; i++) {
  533.             Firing_wait_copy[i] = Robot_info[i].firing_wait[NDL-1];
  534.             Rapidfire_count_copy[i] = Robot_info[i].rapidfire_count[NDL-1];
  535.     
  536.             Robot_info[i].firing_wait[NDL-1] = Robot_info[i].firing_wait[1];
  537.             Robot_info[i].rapidfire_count[NDL-1] = Robot_info[i].rapidfire_count[1];
  538.         }
  539.     }
  540. }
  541.  
  542. void do_lunacy_off(void)
  543. {
  544.     int i;
  545.  
  546.     if ( Lunacy )   {
  547.         Lunacy = 0;
  548.         for (i=0; i<MAX_ROBOT_TYPES; i++) {
  549.             Robot_info[i].firing_wait[NDL-1] = Firing_wait_copy[i];
  550.             Robot_info[i].rapidfire_count[NDL-1] = Rapidfire_count_copy[i];
  551.         }
  552.         Difficulty_level = Diff_save;
  553.     }
  554. }
  555.  
  556. void john_cheat_func_3(int key)
  557. {
  558.     if (!Cheats_enabled)
  559.         return;
  560.  
  561.     if (key == (john_cheats_3[JOHN_CHEATS_SIZE_3 - john_cheats_index_3] ^ (0x61 + john_cheats_index_3))) {
  562.         if (john_cheats_index_3 == 4)
  563.             john_cheats_index_3++;
  564.         john_cheats_index_3++;
  565.         if (john_cheats_index_3 == JOHN_CHEATS_SIZE_3+1) {
  566.             if (Lunacy) {
  567.                 do_lunacy_off();
  568.                 HUD_init_message( TXT_NO_LUNACY );
  569.             } else {
  570.                 do_lunacy_on();
  571.                 HUD_init_message( TXT_LUNACY );
  572.                 digi_play_sample( SOUND_CHEATER, F1_0);
  573.             }
  574.             john_cheats_index_3 = 0;
  575.         }
  576.     } else
  577.         john_cheats_index_3 = 0;
  578. }
  579.  
  580. //  ----------------------------------------------------------------
  581. //  Do *dest = *delta unless:
  582. //              *delta is pretty small
  583. //      and they are of different signs.
  584. void set_rotvel_and_saturate(fix *dest, fix delta)
  585. {
  586.     if ((delta ^ *dest) < 0) {
  587.         if (abs(delta) < F1_0/8) {
  588.             // mprintf((0, "D"));
  589.             *dest = delta/4;
  590.         } else
  591.             // mprintf((0, "d"));
  592.             *dest = delta;
  593.     } else {
  594.         // mprintf((0, "!"));
  595.         *dest = delta;
  596.     }
  597. }
  598.  
  599. //--debug-- #ifndef NDEBUG
  600. //--debug-- int Total_turns=0;
  601. //--debug-- int Prevented_turns=0;
  602. //--debug-- #endif
  603.  
  604. #define AI_TURN_SCALE   1
  605. #define BABY_SPIDER_ID  14
  606.  
  607. extern void physics_turn_towards_vector(vms_vector *goal_vector, object *obj, fix rate);
  608.  
  609. //-------------------------------------------------------------------------------------------
  610. void ai_turn_towards_vector(vms_vector *goal_vector, object *objp, fix rate)
  611. {
  612.     vms_vector  new_fvec;
  613.     fix         dot;
  614.  
  615.     if ((objp->id == BABY_SPIDER_ID) && (objp->type == OBJ_ROBOT)) {
  616.         physics_turn_towards_vector(goal_vector, objp, rate);
  617.         return;
  618.     }
  619.  
  620.     new_fvec = *goal_vector;
  621.  
  622.     dot = vm_vec_dot(goal_vector, &objp->orient.fvec);
  623.  
  624.     if (dot < (F1_0 - FrameTime/2)) {
  625.         fix mag;
  626.         fix new_scale = fixdiv(FrameTime * AI_TURN_SCALE, rate);
  627.         vm_vec_scale(&new_fvec, new_scale);
  628.         vm_vec_add2(&new_fvec, &objp->orient.fvec);
  629.         mag = vm_vec_normalize_quick(&new_fvec);
  630.         if (mag < F1_0/256) {
  631.             mprintf((1, "Degenerate vector in ai_turn_towards_vector (mag = %7.3f)\n", f2fl(mag)));
  632.             new_fvec = *goal_vector;        //  if degenerate vector, go right to goal
  633.         }
  634.     }
  635.  
  636. //  //  Every 8th time, do a correct matrix create, 7/8 time, do a quick one.
  637. //  if (rand() < 0x1000)
  638.         vm_vector_2_matrix(&objp->orient, &new_fvec, NULL, &objp->orient.rvec);
  639. //  else
  640. //      vm_vector_2_matrix_norm(&objp->orient, &new_fvec, NULL, &objp->orient.rvec);
  641.  
  642. //--{
  643. //--vms_vector tvec;
  644. //--fix mag;
  645. //--tvec = objp->orient.fvec;
  646. //--mag = vm_vec_mag(&tvec);
  647. //--mprintf((0, "mags = %7.3f ", f2fl(mag)));
  648. //--
  649. //--tvec = objp->orient.uvec;
  650. //--mag = vm_vec_mag(&tvec);
  651. //--mprintf((0, "%7.3f ", f2fl(mag)));
  652. //--
  653. //--tvec = objp->orient.rvec;
  654. //--mag = vm_vec_mag(&tvec);
  655. //--mprintf((0, "%7.3f\n", f2fl(mag)));
  656. //--}
  657. //--simpler, but buggy: //  The cross product of the forward vector with the right vector is the up vector
  658. //--simpler, but buggy: vm_vec_cross(&new_uvec, &new_fvec, &objp->orient.rvec);
  659. //--simpler, but buggy: vm_vec_cross(&new_rvec, &new_uvec, &new_fvec);
  660. //--simpler, but buggy:
  661. //--simpler, but buggy: objp->orient.fvec = new_fvec;
  662. //--simpler, but buggy: objp->orient.rvec = new_rvec;
  663. //--simpler, but buggy: objp->orient.uvec = new_uvec;
  664. }
  665.  
  666. // --------------------------------------------------------------------------------------------------------------------
  667. void ai_turn_randomly(vms_vector *vec_to_player, object *obj, fix rate, int previous_visibility)
  668. {
  669.     vms_vector  curvec;
  670.  
  671.     //  Random turning looks too stupid, so 1/4 of time, cheat.
  672.     if (previous_visibility)
  673.         if (rand() > 0x7400) {
  674.             ai_turn_towards_vector(vec_to_player, obj, rate);
  675.             return;
  676.         }
  677. //--debug--     if (rand() > 0x6000)
  678. //--debug--         Prevented_turns++;
  679.  
  680.     curvec = obj->mtype.phys_info.rotvel;
  681.  
  682.     curvec.y += F1_0/64;
  683.  
  684.     curvec.x += curvec.y/6;
  685.     curvec.y += curvec.z/4;
  686.     curvec.z += curvec.x/10;
  687.  
  688.     if (abs(curvec.x) > F1_0/8) curvec.x /= 4;
  689.     if (abs(curvec.y) > F1_0/8) curvec.y /= 4;
  690.     if (abs(curvec.z) > F1_0/8) curvec.z /= 4;
  691.  
  692.     obj->mtype.phys_info.rotvel = curvec;
  693.  
  694. }
  695.  
  696. //  Overall_agitation affects:
  697. //      Widens field of view.  Field of view is in range 0..1 (specified in bitmaps.tbl as N/360 degrees).
  698. //          Overall_agitation/128 subtracted from field of view, making robots see wider.
  699. //      Increases distance to which robot will search to create path to player by Overall_agitation/8 segments.
  700. //      Decreases wait between fire times by Overall_agitation/64 seconds.
  701.  
  702. void john_cheat_func_4(int key)
  703. {
  704.     if (!Cheats_enabled)
  705.         return;
  706.  
  707.     switch (john_cheats_index_4) {
  708.         case 3:
  709.             if (key == KEY_T)
  710.                 john_cheats_index_4++;
  711.             else
  712.                 john_cheats_index_4 = 0;
  713.             break;
  714.  
  715.         case 1:
  716.             if (key == KEY_L)
  717.                 john_cheats_index_4++;
  718.             else
  719.                 john_cheats_index_4 = 0;
  720.             break;
  721.     
  722.         case 2:
  723.             if (key == KEY_E)
  724.                 john_cheats_index_4++;
  725.             else
  726.                 john_cheats_index_4 = 0;
  727.             break;
  728.     
  729.         case 0:
  730.             if (key == KEY_P)
  731.                 john_cheats_index_4++;
  732.             break;
  733.  
  734.  
  735.         case 4:
  736.             if (key == KEY_C)
  737.                 john_cheats_index_4++;
  738.             else
  739.                 john_cheats_index_4 = 0;
  740.             break;
  741.     
  742.         case 5:
  743.             if (key == KEY_H)
  744.                 john_cheats_index_4++;
  745.             else
  746.                 john_cheats_index_4 = 0;
  747.             break;
  748.     
  749.         case 6:
  750.             Ugly_robot_texture = 0;
  751.         case 7:
  752.         case 8:
  753.             if ((key >= KEY_1) && (key <= KEY_0)) {
  754.                 john_cheats_index_4++;
  755.                 Ugly_robot_texture *= 10;
  756.                 if (key != KEY_0)
  757.                     Ugly_robot_texture += key - 1;
  758.                 if (john_cheats_index_4 == 9) {
  759.                     if (Ugly_robot_texture == 999)  {
  760.                         Ugly_robot_cheat = 0;
  761.                         HUD_init_message( TXT_ROBOT_PAINTING_OFF );
  762.                     } else {
  763.                         HUD_init_message( TXT_ROBOT_PAINTING_ON, Ugly_robot_texture );
  764.                         Ugly_robot_cheat = 0xBADA55;
  765.                     }
  766.                     mprintf((0, "Paint value = %i\n", Ugly_robot_texture));
  767.                     john_cheats_index_4 = 0;
  768.                 }
  769.             } else
  770.                 john_cheats_index_4 = 0;
  771.         
  772.             break;
  773.         default:
  774.             john_cheats_index_4 = 0;
  775.     }
  776. }
  777.  
  778. // --------------------------------------------------------------------------------------------------------------------
  779. //  Returns:
  780. //      0       Player is not visible from object, obstruction or something.
  781. //      1       Player is visible, but not in field of view.
  782. //      2       Player is visible and in field of view.
  783. //  Note: Uses Believed_player_pos as player's position for cloak effect.
  784. //  NOTE: Will destructively modify *pos if *pos is outside the mine.
  785. int player_is_visible_from_object(object *objp, vms_vector *pos, fix field_of_view, vms_vector *vec_to_player)
  786. {
  787.     fix         dot;
  788.     fvi_query   fq;
  789.  
  790.     fq.p0                       = pos;
  791.     if ((pos->x != objp->pos.x) || (pos->y != objp->pos.y) || (pos->z != objp->pos.z)) {
  792.         int segnum = find_point_seg(pos, objp->segnum);
  793.         if (segnum == -1) {
  794.             fq.startseg = objp->segnum;
  795.             *pos = objp->pos;
  796.             mprintf((1, "Object %i, gun is outside mine, moving towards center.\n", objp-Objects));
  797.             move_towards_segment_center(objp);
  798.         } else
  799.             fq.startseg = segnum;
  800.     } else
  801.         fq.startseg         = objp->segnum;
  802.     fq.p1                       = &Believed_player_pos;
  803.     fq.rad                  = F1_0/4;
  804.     fq.thisobjnum           = objp-Objects;
  805.     fq.ignore_obj_list  = NULL;
  806.     fq.flags                    = FQ_TRANSWALL | FQ_CHECK_OBJS;     //what about trans walls???
  807.  
  808.     Hit_type = find_vector_intersection(&fq,&Hit_data);
  809.  
  810.     Hit_pos = Hit_data.hit_pnt;
  811.     Hit_seg = Hit_data.hit_seg;
  812.  
  813.     if ((Hit_type == HIT_NONE) || ((Hit_type == HIT_OBJECT) && (Hit_data.hit_object == Players[Player_num].objnum))) {
  814.         dot = vm_vec_dot(vec_to_player, &objp->orient.fvec);
  815.         // mprintf((0, "Fvec = [%5.2f %5.2f %5.2f], vec_to_player = [%5.2f %5.2f %5.2f], dot = %7.3f\n", f2fl(objp->orient.fvec.x), f2fl(objp->orient.fvec.y), f2fl(objp->orient.fvec.z), f2fl(vec_to_player->x), f2fl(vec_to_player->y), f2fl(vec_to_player->z), f2fl(dot)));
  816.         if (dot > field_of_view - (Overall_agitation << 9)) {
  817.             // mprintf((0, "I can see you!\n"));
  818.             return 2;
  819.         } else {
  820.             // mprintf((0, "Damn, I could see you if I were looking...\n"));
  821.             return 1;
  822.         }
  823.     } else {
  824.         // mprintf((0, " ** Where are you? **\n"));
  825.         return 0;
  826.     }
  827. }
  828.  
  829. // ------------------------------------------------------------------------------------------------------------------
  830. //  Return 1 if animates, else return 0
  831. int do_silly_animation(object *objp)
  832. {
  833.     int             objnum = objp-Objects;
  834.     jointpos        *jp_list;
  835.     int             robot_type, gun_num, robot_state, num_joint_positions;
  836.     polyobj_info    *pobj_info = &objp->rtype.pobj_info;
  837.     ai_static       *aip = &objp->ctype.ai_info;
  838.     // ai_local         *ailp = &Ai_local_info[objnum];
  839.     int             num_guns, at_goal;
  840.     int             attack_type;
  841.     int             flinch_attack_scale = 1;
  842.  
  843.     robot_type = objp->id;
  844.     num_guns = Robot_info[robot_type].n_guns;
  845.     attack_type = Robot_info[robot_type].attack_type;
  846.  
  847.     if (num_guns == 0) {
  848.         // mprintf((0, "Object #%i of type #%i has 0 guns.\n", objp-Objects, robot_type));
  849.         return 0;
  850.     }
  851.  
  852.     //  This is a hack.  All positions should be based on goal_state, not GOAL_STATE.
  853.     robot_state = Mike_to_matt_xlate[aip->GOAL_STATE];
  854.     // previous_robot_state = Mike_to_matt_xlate[aip->CURRENT_STATE];
  855.  
  856.     if (attack_type) // && ((robot_state == AS_FIRE) || (robot_state == AS_RECOIL)))
  857.         flinch_attack_scale = Attack_scale;
  858.     else if ((robot_state == AS_FLINCH) || (robot_state == AS_RECOIL))
  859.         flinch_attack_scale = Flinch_scale;
  860.  
  861.     at_goal = 1;
  862.     for (gun_num=0; gun_num <= num_guns; gun_num++) {
  863.         int joint;
  864.  
  865.         num_joint_positions = robot_get_anim_state(&jp_list, robot_type, gun_num, robot_state);
  866.  
  867.         for (joint=0; joint<num_joint_positions; joint++) {
  868.             fix         delta_angle, delta_2;
  869.             int         jointnum = jp_list[joint].jointnum;
  870.             vms_angvec  *jp = &jp_list[joint].angles;
  871.             vms_angvec  *pobjp = &pobj_info->anim_angles[jointnum];
  872.  
  873.             if (jointnum >= Polygon_models[objp->rtype.pobj_info.model_num].n_models) {
  874.                 Int3();     // Contact Mike: incompatible data, illegal jointnum, problem in pof file?
  875.                 continue;
  876.             }
  877.             if (jp->p != pobjp->p) {
  878.                 if (gun_num == 0)
  879.                     at_goal = 0;
  880.                 Ai_local_info[objnum].goal_angles[jointnum].p = jp->p;
  881.  
  882.                 delta_angle = jp->p - pobjp->p;
  883.                 if (delta_angle >= F1_0/2)
  884.                     delta_2 = -ANIM_RATE;
  885.                 else if (delta_angle >= 0)
  886.                     delta_2 = ANIM_RATE;
  887.                 else if (delta_angle >= -F1_0/2)
  888.                     delta_2 = -ANIM_RATE;
  889.                 else
  890.                     delta_2 = ANIM_RATE;
  891.  
  892.                 if (flinch_attack_scale != 1)
  893.                     delta_2 *= flinch_attack_scale;
  894.  
  895.                 Ai_local_info[objnum].delta_angles[jointnum].p = delta_2/DELTA_ANG_SCALE;       // complete revolutions per second
  896.             }
  897.  
  898.             if (jp->b != pobjp->b) {
  899.                 if (gun_num == 0)
  900.                     at_goal = 0;
  901.                 Ai_local_info[objnum].goal_angles[jointnum].b = jp->b;
  902.  
  903.                 delta_angle = jp->b - pobjp->b;
  904.                 if (delta_angle >= F1_0/2)
  905.                     delta_2 = -ANIM_RATE;
  906.                 else if (delta_angle >= 0)
  907.                     delta_2 = ANIM_RATE;
  908.                 else if (delta_angle >= -F1_0/2)
  909.                     delta_2 = -ANIM_RATE;
  910.                 else
  911.                     delta_2 = ANIM_RATE;
  912.  
  913.                 if (flinch_attack_scale != 1)
  914.                     delta_2 *= flinch_attack_scale;
  915.  
  916.                 Ai_local_info[objnum].delta_angles[jointnum].b = delta_2/DELTA_ANG_SCALE;       // complete revolutions per second
  917.             }
  918.  
  919.             if (jp->h != pobjp->h) {
  920.                 if (gun_num == 0)
  921.                     at_goal = 0;
  922.                 Ai_local_info[objnum].goal_angles[jointnum].h = jp->h;
  923.  
  924.                 delta_angle = jp->h - pobjp->h;
  925.                 if (delta_angle >= F1_0/2)
  926.                     delta_2 = -ANIM_RATE;
  927.                 else if (delta_angle >= 0)
  928.                     delta_2 = ANIM_RATE;
  929.                 else if (delta_angle >= -F1_0/2)
  930.                     delta_2 = -ANIM_RATE;
  931.                 else
  932.                     delta_2 = ANIM_RATE;
  933.  
  934.                 if (flinch_attack_scale != 1)
  935.                     delta_2 *= flinch_attack_scale;
  936.  
  937.                 Ai_local_info[objnum].delta_angles[jointnum].h = delta_2/DELTA_ANG_SCALE;       // complete revolutions per second
  938.             }
  939.         }
  940.  
  941.         if (at_goal) {
  942.             //ai_static *aip = &objp->ctype.ai_info;
  943.             ai_local        *ailp = &Ai_local_info[objp-Objects];
  944.             ailp->achieved_state[gun_num] = ailp->goal_state[gun_num];
  945.             if (ailp->achieved_state[gun_num] == AIS_RECO)
  946.                 ailp->goal_state[gun_num] = AIS_FIRE;
  947.  
  948.             if (ailp->achieved_state[gun_num] == AIS_FLIN)
  949.                 ailp->goal_state[gun_num] = AIS_LOCK;
  950.  
  951.         }
  952.     }
  953.  
  954.     if (at_goal == 1) //num_guns)
  955.         aip->CURRENT_STATE = aip->GOAL_STATE;
  956.  
  957.     return 1;
  958. }
  959.  
  960. //  ------------------------------------------------------------------------------------------
  961. //  Move all sub-objects in an object towards their goals.
  962. //  Current orientation of object is at:    pobj_info.anim_angles
  963. //  Goal orientation of object is at:       ai_info.goal_angles
  964. //  Delta orientation of object is at:      ai_info.delta_angles
  965. void ai_frame_animation(object *objp)
  966. {
  967.     int objnum = objp-Objects;
  968.     int joint;
  969.     int num_joints;
  970.  
  971.     num_joints = Polygon_models[objp->rtype.pobj_info.model_num].n_models;
  972.  
  973.     for (joint=1; joint<num_joints; joint++) {
  974.         fix         delta_to_goal;
  975.         fix         scaled_delta_angle;
  976.         vms_angvec  *curangp = &objp->rtype.pobj_info.anim_angles[joint];
  977.         vms_angvec  *goalangp = &Ai_local_info[objnum].goal_angles[joint];
  978.         vms_angvec  *deltaangp = &Ai_local_info[objnum].delta_angles[joint];
  979.  
  980. #ifndef NDEBUG
  981. if (Ai_animation_test) {
  982.     printf("%i: [%7.3f %7.3f %7.3f]  [%7.3f %7.3f %7.3f]\n", joint, f2fl(curangp->p), f2fl(curangp->b), f2fl(curangp->h), f2fl(goalangp->p), f2fl(goalangp->b), f2fl(goalangp->h), f2fl(curangp->p), f2fl(curangp->b), f2fl(curangp->h));
  983. }
  984. #endif
  985.         delta_to_goal = goalangp->p - curangp->p;
  986.         if (delta_to_goal > 32767)
  987.             delta_to_goal = delta_to_goal - 65536;
  988.         else if (delta_to_goal < -32767)
  989.             delta_to_goal = 65536 + delta_to_goal;
  990.  
  991.         if (delta_to_goal) {
  992.             scaled_delta_angle = fixmul(deltaangp->p, FrameTime) * DELTA_ANG_SCALE;
  993.             curangp->p += scaled_delta_angle;
  994.             if (abs(delta_to_goal) < abs(scaled_delta_angle))
  995.                 curangp->p = goalangp->p;
  996.         }
  997.  
  998.         delta_to_goal = goalangp->b - curangp->b;
  999.         if (delta_to_goal > 32767)
  1000.             delta_to_goal = delta_to_goal - 65536;
  1001.         else if (delta_to_goal < -32767)
  1002.             delta_to_goal = 65536 + delta_to_goal;
  1003.  
  1004.         if (delta_to_goal) {
  1005.             scaled_delta_angle = fixmul(deltaangp->b, FrameTime) * DELTA_ANG_SCALE;
  1006.             curangp->b += scaled_delta_angle;
  1007.             if (abs(delta_to_goal) < abs(scaled_delta_angle))
  1008.                 curangp->b = goalangp->b;
  1009.         }
  1010.  
  1011.         delta_to_goal = goalangp->h - curangp->h;
  1012.         if (delta_to_goal > 32767)
  1013.             delta_to_goal = delta_to_goal - 65536;
  1014.         else if (delta_to_goal < -32767)
  1015.             delta_to_goal = 65536 + delta_to_goal;
  1016.  
  1017.         if (delta_to_goal) {
  1018.             scaled_delta_angle = fixmul(deltaangp->h, FrameTime) * DELTA_ANG_SCALE;
  1019.             curangp->h += scaled_delta_angle;
  1020.             if (abs(delta_to_goal) < abs(scaled_delta_angle))
  1021.                 curangp->h = goalangp->h;
  1022.         }
  1023.  
  1024.     }
  1025.  
  1026. }
  1027.  
  1028. // ----------------------------------------------------------------------------------
  1029. void set_next_fire_time(ai_local *ailp, robot_info *robptr)
  1030. {
  1031.     ailp->rapidfire_count++;
  1032.  
  1033.     if (ailp->rapidfire_count < robptr->rapidfire_count[Difficulty_level]) {
  1034.         ailp->next_fire = min(F1_0/8, robptr->firing_wait[Difficulty_level]/2);
  1035.     } else {
  1036.         ailp->rapidfire_count = 0;
  1037.         ailp->next_fire = robptr->firing_wait[Difficulty_level];
  1038.     }
  1039. }
  1040.  
  1041. // ----------------------------------------------------------------------------------
  1042. //  When some robots collide with the player, they attack.
  1043. //  If player is cloaked, then robot probably didn't actually collide, deal with that here.
  1044. void do_ai_robot_hit_attack(object *robot, object *player, vms_vector *collision_point)
  1045. {
  1046.     ai_local        *ailp = &Ai_local_info[robot-Objects];
  1047.     robot_info *robptr = &Robot_info[robot->id];
  1048.  
  1049. //#ifndef NDEBUG
  1050.     if (!Robot_firing_enabled)
  1051.         return;
  1052. //#endif
  1053.  
  1054.     //  If player is dead, stop firing.
  1055.     if (Objects[Players[Player_num].objnum].type == OBJ_GHOST)
  1056.         return;
  1057.  
  1058.     if (robptr->attack_type == 1) {
  1059.         if (ailp->next_fire <= 0) {
  1060.             if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
  1061.                 if (vm_vec_dist_quick(&ConsoleObject->pos, &robot->pos) < robot->size + ConsoleObject->size + F1_0*2)
  1062.                     collide_player_and_nasty_robot( player, robot, collision_point );
  1063.  
  1064.             robot->ctype.ai_info.GOAL_STATE = AIS_RECO;
  1065.             set_next_fire_time(ailp, robptr);
  1066.         }
  1067.     }
  1068.  
  1069. }
  1070.  
  1071. extern int Player_exploded;
  1072.  
  1073. // --------------------------------------------------------------------------------------------------------------------
  1074. //  Note: Parameter vec_to_player is only passed now because guns which aren't on the forward vector from the
  1075. //  center of the robot will not fire right at the player.  We need to aim the guns at the player.  Barring that, we cheat.
  1076. //  When this routine is complete, the parameter vec_to_player should not be necessary.
  1077. void ai_fire_laser_at_player(object *obj, vms_vector *fire_point)
  1078. {
  1079.     int         objnum = obj-Objects;
  1080.     ai_local        *ailp = &Ai_local_info[objnum];
  1081.     robot_info  *robptr = &Robot_info[obj->id];
  1082.     vms_vector  fire_vec;
  1083.     vms_vector  bpp_diff;
  1084.  
  1085.     if (!Robot_firing_enabled)
  1086.         return;
  1087.  
  1088. #ifndef NDEBUG
  1089.     //  We should never be coming here for the green guy, as he has no laser!
  1090.     if (robptr->attack_type == 1)
  1091.         Int3(); // Contact Mike: This is impossible.
  1092. #endif
  1093.  
  1094.     if (obj->control_type == CT_MORPH)
  1095.         return;
  1096.  
  1097.     //  If player is exploded, stop firing.
  1098.     if (Player_exploded)
  1099.         return;
  1100.  
  1101.     //  If player is cloaked, maybe don't fire based on how long cloaked and randomness.
  1102.     if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
  1103.         fix cloak_time = Ai_cloak_info[objnum % MAX_AI_CLOAK_INFO].last_time;
  1104.  
  1105.         if (GameTime - cloak_time > CLOAK_TIME_MAX/4)
  1106.             if (rand() > fixdiv(GameTime - cloak_time, CLOAK_TIME_MAX)/2) {
  1107.                 set_next_fire_time(ailp, robptr);
  1108.                 return;
  1109.             }
  1110.     }
  1111.  
  1112. //--    //  Find segment containing laser fire position.  If the robot is straddling a segment, the position from
  1113. //--    //  which it fires may be in a different segment, which is bad news for find_vector_intersection.  So, cast
  1114. //--    //  a ray from the object center (whose segment we know) to the laser position.  Then, in the call to Laser_create_new
  1115. //--    //  use the data returned from this call to find_vector_intersection.
  1116. //--    //  Note that while find_vector_intersection is pretty slow, it is not terribly slow if the destination point is
  1117. //--    //  in the same segment as the source point.
  1118. //--
  1119. //--    fq.p0                       = &obj->pos;
  1120. //--    fq.startseg             = obj->segnum;
  1121. //--    fq.p1                       = fire_point;
  1122. //--    fq.rad                  = 0;
  1123. //--    fq.thisobjnum           = obj-Objects;
  1124. //--    fq.ignore_obj_list  = NULL;
  1125. //--    fq.flags                    = FQ_TRANSWALL | FQ_CHECK_OBJS;     //what about trans walls???
  1126. //--
  1127. //--    fate = find_vector_intersection(&fq, &hit_data);
  1128. //--    if (fate != HIT_NONE)
  1129. //--        return;
  1130.  
  1131.     //  Set position to fire at based on difficulty level.
  1132.     bpp_diff.x = Believed_player_pos.x + (rand()-16384) * (NDL-Difficulty_level-1) * 4;
  1133.     bpp_diff.y = Believed_player_pos.y + (rand()-16384) * (NDL-Difficulty_level-1) * 4;
  1134.     bpp_diff.z = Believed_player_pos.z + (rand()-16384) * (NDL-Difficulty_level-1) * 4;
  1135.  
  1136.     //  Half the time fire at the player, half the time lead the player.
  1137.     if (rand() > 16384) {
  1138.  
  1139.         vm_vec_normalized_dir_quick(&fire_vec, &bpp_diff, fire_point);
  1140.  
  1141.     } else {
  1142.         vms_vector  player_direction_vector;
  1143.  
  1144.         vm_vec_sub(&player_direction_vector, &bpp_diff, &bpp_diff);
  1145.  
  1146.         // If player is not moving, fire right at him!
  1147.         //  Note: If the robot fires in the direction of its forward vector, this is bad because the weapon does not
  1148.         //  come out from the center of the robot; it comes out from the side.  So it is common for the weapon to miss
  1149.         //  its target.  Ideally, we want to point the guns at the player.  For now, just fire right at the player.
  1150.         if ((abs(player_direction_vector.x < 0x10000)) && (abs(player_direction_vector.y < 0x10000)) && (abs(player_direction_vector.z < 0x10000))) {
  1151.  
  1152.             vm_vec_normalized_dir_quick(&fire_vec, &bpp_diff, fire_point);
  1153.  
  1154.         // Player is moving.  Determine where the player will be at the end of the next frame if he doesn't change his
  1155.         //  behavior.  Fire at exactly that point.  This isn't exactly what you want because it will probably take the laser
  1156.         //  a different amount of time to get there, since it will probably be a different distance from the player.
  1157.         //  So, that's why we write games, instead of guiding missiles...
  1158.         } else {
  1159.             vm_vec_sub(&fire_vec, &bpp_diff, fire_point);
  1160.             vm_vec_scale(&fire_vec,fixmul(Weapon_info[Robot_info[obj->id].weapon_type].speed[Difficulty_level], FrameTime));
  1161.  
  1162.             vm_vec_add2(&fire_vec, &player_direction_vector);
  1163.             vm_vec_normalize_quick(&fire_vec);
  1164.  
  1165.         }
  1166.     }
  1167.  
  1168. //#ifndef NDEBUG
  1169. //  if (robptr->boss_flag)
  1170. //      mprintf((0, "Boss (%i) fires!\n", obj-Objects));
  1171. //#endif
  1172.  
  1173.     Laser_create_new_easy( &fire_vec, fire_point, obj-Objects, robptr->weapon_type, 1);
  1174.  
  1175. #ifndef SHAREWARE
  1176. #ifdef NETWORK
  1177.     if (Game_mode & GM_MULTI)
  1178.     {
  1179.         ai_multi_send_robot_position(objnum, -1);
  1180.         multi_send_robot_fire(objnum, obj->ctype.ai_info.CURRENT_GUN, &fire_vec);
  1181.     }
  1182. #endif
  1183. #endif
  1184.  
  1185.     create_awareness_event(obj, PA_NEARBY_ROBOT_FIRED);
  1186.  
  1187.     set_next_fire_time(ailp, robptr);
  1188.  
  1189.     //  If the boss fired, allow him to teleport very soon (right after firing, cool!), pending other factors.
  1190.     if (robptr->boss_flag)
  1191.         Last_teleport_time -= Boss_teleport_interval/2;
  1192. }
  1193.  
  1194. // --------------------------------------------------------------------------------------------------------------------
  1195. //  vec_goal must be normalized, or close to it.
  1196. void move_towards_vector(object *objp, vms_vector *vec_goal)
  1197. {
  1198.     physics_info    *pptr = &objp->mtype.phys_info;
  1199.     fix             speed, dot, max_speed;
  1200.     robot_info      *robptr = &Robot_info[objp->id];
  1201.     vms_vector      vel;
  1202.  
  1203.     //  Trying to move towards player.  If forward vector much different than velocity vector,
  1204.     //  bash velocity vector twice as much towards player as usual.
  1205.  
  1206.     vel = pptr->velocity;
  1207.     vm_vec_normalize_quick(&vel);
  1208.     dot = vm_vec_dot(&vel, &objp->orient.fvec);
  1209.  
  1210.     if (dot < 3*F1_0/4) {
  1211.         //  This funny code is supposed to slow down the robot and move his velocity towards his direction
  1212.         //  more quickly than the general code
  1213.         //-! mprintf((0, "Th  "));
  1214.         pptr->velocity.x = pptr->velocity.x/2 + fixmul(vec_goal->x, FrameTime*32);
  1215.         pptr->velocity.y = pptr->velocity.y/2 + fixmul(vec_goal->y, FrameTime*32);
  1216.         pptr->velocity.z = pptr->velocity.z/2 + fixmul(vec_goal->z, FrameTime*32);
  1217.     } else {
  1218.         //-! mprintf((0, "Tn  "));
  1219.         pptr->velocity.x += fixmul(vec_goal->x, FrameTime*64) * (Difficulty_level+5)/4;
  1220.         pptr->velocity.y += fixmul(vec_goal->y, FrameTime*64) * (Difficulty_level+5)/4;
  1221.         pptr->velocity.z += fixmul(vec_goal->z, FrameTime*64) * (Difficulty_level+5)/4;
  1222.     }
  1223.  
  1224.     speed = vm_vec_mag_quick(&pptr->velocity);
  1225.     max_speed = robptr->max_speed[Difficulty_level];
  1226.  
  1227.     //  Green guy attacks twice as fast as he moves away.
  1228.     if (robptr->attack_type == 1)
  1229.         max_speed *= 2;
  1230.  
  1231.     if (speed > max_speed) {
  1232.         pptr->velocity.x = (pptr->velocity.x*3)/4;
  1233.         pptr->velocity.y = (pptr->velocity.y*3)/4;
  1234.         pptr->velocity.z = (pptr->velocity.z*3)/4;
  1235.     }
  1236. }
  1237.  
  1238. // --------------------------------------------------------------------------------------------------------------------
  1239. void move_towards_player(object *objp, vms_vector *vec_to_player)
  1240. //  vec_to_player must be normalized, or close to it.
  1241. {
  1242.     move_towards_vector(objp, vec_to_player);
  1243. }
  1244.  
  1245. // --------------------------------------------------------------------------------------------------------------------
  1246. //  I am ashamed of this: fast_flag == -1 means normal slide about.  fast_flag = 0 means no evasion.
  1247. void move_around_player(object *objp, vms_vector *vec_to_player, int fast_flag)
  1248. {
  1249.     physics_info    *pptr = &objp->mtype.phys_info;
  1250.     fix             speed;
  1251.     robot_info      *robptr = &Robot_info[objp->id];
  1252.     int             objnum = objp-Objects;
  1253.     int             dir;
  1254.     int             dir_change;
  1255.     fix             ft;
  1256.     vms_vector      evade_vector;
  1257.     int             count=0;
  1258.  
  1259.     if (fast_flag == 0)
  1260.         return;
  1261.  
  1262.     dir_change = 48;
  1263.     ft = FrameTime;
  1264.     if (ft < F1_0/32) {
  1265.         dir_change *= 8;
  1266.         count += 3;
  1267.     } else
  1268.         while (ft < F1_0/4) {
  1269.             dir_change *= 2;
  1270.             ft *= 2;
  1271.             count++;
  1272.         }
  1273.  
  1274.     dir = (FrameCount + (count+1) * (objnum*8 + objnum*4 + objnum)) & dir_change;
  1275.     dir >>= (4+count);
  1276.  
  1277.     Assert((dir >= 0) && (dir <= 3));
  1278.  
  1279.     switch (dir) {
  1280.         case 0:
  1281.             evade_vector.x = fixmul(vec_to_player->z, FrameTime*32);
  1282.             evade_vector.y = fixmul(vec_to_player->y, FrameTime*32);
  1283.             evade_vector.z = fixmul(-vec_to_player->x, FrameTime*32);
  1284.             break;
  1285.         case 1:
  1286.             evade_vector.x = fixmul(-vec_to_player->z, FrameTime*32);
  1287.             evade_vector.y = fixmul(vec_to_player->y, FrameTime*32);
  1288.             evade_vector.z = fixmul(vec_to_player->x, FrameTime*32);
  1289.             break;
  1290.         case 2:
  1291.             evade_vector.x = fixmul(-vec_to_player->y, FrameTime*32);
  1292.             evade_vector.y = fixmul(vec_to_player->x, FrameTime*32);
  1293.             evade_vector.z = fixmul(vec_to_player->z, FrameTime*32);
  1294.             break;
  1295.         case 3:
  1296.             evade_vector.x = fixmul(vec_to_player->y, FrameTime*32);
  1297.             evade_vector.y = fixmul(-vec_to_player->x, FrameTime*32);
  1298.             evade_vector.z = fixmul(vec_to_player->z, FrameTime*32);
  1299.             break;
  1300.     }
  1301.  
  1302.     //  Note: -1 means normal circling about the player.  > 0 means fast evasion.
  1303.     if (fast_flag > 0) {
  1304.         fix dot;
  1305.  
  1306.         //  Only take evasive action if looking at player.
  1307.         //  Evasion speed is scaled by percentage of shields left so wounded robots evade less effectively.
  1308.  
  1309.         dot = vm_vec_dot(vec_to_player, &objp->orient.fvec);
  1310.         if ((dot > robptr->field_of_view[Difficulty_level]) && !(ConsoleObject->flags & PLAYER_FLAGS_CLOAKED)) {
  1311.             fix damage_scale;
  1312.  
  1313.             damage_scale = fixdiv(objp->shields, robptr->strength);
  1314.             if (damage_scale > F1_0)
  1315.                 damage_scale = F1_0;        //  Just in case...
  1316.             else if (damage_scale < 0)
  1317.                 damage_scale = 0;           //  Just in case...
  1318.  
  1319.             vm_vec_scale(&evade_vector, i2f(fast_flag) + damage_scale);
  1320.         }
  1321.     }
  1322.  
  1323.     pptr->velocity.x += evade_vector.x;
  1324.     pptr->velocity.y += evade_vector.y;
  1325.     pptr->velocity.z += evade_vector.z;
  1326.  
  1327.     speed = vm_vec_mag_quick(&pptr->velocity);
  1328.     if (speed > robptr->max_speed[Difficulty_level]) {
  1329.         pptr->velocity.x = (pptr->velocity.x*3)/4;
  1330.         pptr->velocity.y = (pptr->velocity.y*3)/4;
  1331.         pptr->velocity.z = (pptr->velocity.z*3)/4;
  1332.     }
  1333.  
  1334. }
  1335.  
  1336. // --------------------------------------------------------------------------------------------------------------------
  1337. void move_away_from_player(object *objp, vms_vector *vec_to_player, int attack_type)
  1338. {
  1339.     fix             speed;
  1340.     physics_info    *pptr = &objp->mtype.phys_info;
  1341.     robot_info      *robptr = &Robot_info[objp->id];
  1342.     int             objref;
  1343.  
  1344.     pptr->velocity.x -= fixmul(vec_to_player->x, FrameTime*16);
  1345.     pptr->velocity.y -= fixmul(vec_to_player->y, FrameTime*16);
  1346.     pptr->velocity.z -= fixmul(vec_to_player->z, FrameTime*16);
  1347.  
  1348.     if (attack_type) {
  1349.         //  Get value in 0..3 to choose evasion direction.
  1350.         objref = ((objp-Objects) ^ ((FrameCount + 3*(objp-Objects)) >> 5)) & 3;
  1351.  
  1352.         switch (objref) {
  1353.             case 0: vm_vec_scale_add2(&pptr->velocity, &objp->orient.uvec, FrameTime << 5); break;
  1354.             case 1: vm_vec_scale_add2(&pptr->velocity, &objp->orient.uvec, -FrameTime << 5);    break;
  1355.             case 2: vm_vec_scale_add2(&pptr->velocity, &objp->orient.rvec, FrameTime << 5); break;
  1356.             case 3: vm_vec_scale_add2(&pptr->velocity, &objp->orient.rvec, -FrameTime << 5);    break;
  1357.             default:    Int3(); //  Impossible, bogus value on objref, must be in 0..3
  1358.         }
  1359.     }
  1360.  
  1361.  
  1362.     speed = vm_vec_mag_quick(&pptr->velocity);
  1363.  
  1364.     if (speed > robptr->max_speed[Difficulty_level]) {
  1365.         pptr->velocity.x = (pptr->velocity.x*3)/4;
  1366.         pptr->velocity.y = (pptr->velocity.y*3)/4;
  1367.         pptr->velocity.z = (pptr->velocity.z*3)/4;
  1368.     }
  1369.  
  1370. //--old--   fix             speed, dot;
  1371. //--old--   physics_info    *pptr = &objp->mtype.phys_info;
  1372. //--old--   robot_info      *robptr = &Robot_info[objp->id];
  1373. //--old--
  1374. //--old--   //  Trying to move away from player.  If forward vector much different than velocity vector,
  1375. //--old--   //  bash velocity vector twice as much away from player as usual.
  1376. //--old--   dot = vm_vec_dot(&pptr->velocity, &objp->orient.fvec);
  1377. //--old--   if (dot > -3*F1_0/4) {
  1378. //--old--       //  This funny code is supposed to slow down the robot and move his velocity towards his direction
  1379. //--old--       //  more quickly than the general code
  1380. //--old--       pptr->velocity.x = pptr->velocity.x/2 - fixmul(vec_to_player->x, FrameTime*16);
  1381. //--old--       pptr->velocity.y = pptr->velocity.y/2 - fixmul(vec_to_player->y, FrameTime*16);
  1382. //--old--       pptr->velocity.z = pptr->velocity.z/2 - fixmul(vec_to_player->z, FrameTime*16);
  1383. //--old--   } else {
  1384. //--old--       pptr->velocity.x -= fixmul(vec_to_player->x, FrameTime*16);
  1385. //--old--       pptr->velocity.y -= fixmul(vec_to_player->y, FrameTime*16);
  1386. //--old--       pptr->velocity.z -= fixmul(vec_to_player->z, FrameTime*16);
  1387. //--old--   }
  1388. //--old--
  1389. //--old--   speed = vm_vec_mag_quick(&pptr->velocity);
  1390. //--old--
  1391. //--old--   if (speed > robptr->max_speed[Difficulty_level]) {
  1392. //--old--       pptr->velocity.x = (pptr->velocity.x*3)/4;
  1393. //--old--       pptr->velocity.y = (pptr->velocity.y*3)/4;
  1394. //--old--       pptr->velocity.z = (pptr->velocity.z*3)/4;
  1395. //--old--   }
  1396. }
  1397.  
  1398. // --------------------------------------------------------------------------------------------------------------------
  1399. //  Move towards, away_from or around player.
  1400. //  Also deals with evasion.
  1401. //  If the flag evade_only is set, then only allowed to evade, not allowed to move otherwise (must have mode == AIM_STILL).
  1402. void ai_move_relative_to_player(object *objp, ai_local *ailp, fix dist_to_player, vms_vector *vec_to_player, fix circle_distance, int evade_only)
  1403. {
  1404.     object      *dobjp;
  1405.     robot_info  *robptr = &Robot_info[objp->id];
  1406.  
  1407.     //  See if should take avoidance.
  1408.  
  1409. // New way, green guys don't evade: if ((robptr->attack_type == 0) && (objp->ctype.ai_info.danger_laser_num != -1)) {
  1410.     if (objp->ctype.ai_info.danger_laser_num != -1) {
  1411.         dobjp = &Objects[objp->ctype.ai_info.danger_laser_num];
  1412.  
  1413.         if ((dobjp->type == OBJ_WEAPON) && (dobjp->signature == objp->ctype.ai_info.danger_laser_signature)) {
  1414.             fix         dot, dist_to_laser, field_of_view;
  1415.             vms_vector  vec_to_laser, laser_fvec;
  1416.  
  1417.             field_of_view = Robot_info[objp->id].field_of_view[Difficulty_level];
  1418.  
  1419.             vm_vec_sub(&vec_to_laser, &dobjp->pos, &objp->pos);
  1420.             dist_to_laser = vm_vec_normalize_quick(&vec_to_laser);
  1421.             dot = vm_vec_dot(&vec_to_laser, &objp->orient.fvec);
  1422.  
  1423.             if (dot > field_of_view) {
  1424.                 fix         laser_robot_dot;
  1425.                 vms_vector  laser_vec_to_robot;
  1426.  
  1427.                 //  The laser is seen by the robot, see if it might hit the robot.
  1428.                 //  Get the laser's direction.  If it's a polyobj, it can be gotten cheaply from the orientation matrix.
  1429.                 if (dobjp->render_type == RT_POLYOBJ)
  1430.                     laser_fvec = dobjp->orient.fvec;
  1431.                 else {      //  Not a polyobj, get velocity and normalize.
  1432.                     laser_fvec = dobjp->mtype.phys_info.velocity;   //dobjp->orient.fvec;
  1433.                     vm_vec_normalize_quick(&laser_fvec);
  1434.                 }
  1435.                 vm_vec_sub(&laser_vec_to_robot, &objp->pos, &dobjp->pos);
  1436.                 vm_vec_normalize_quick(&laser_vec_to_robot);
  1437.                 laser_robot_dot = vm_vec_dot(&laser_fvec, &laser_vec_to_robot);
  1438.  
  1439.                 if ((laser_robot_dot > F1_0*7/8) && (dist_to_laser < F1_0*80)) {
  1440.                     int evade_speed;
  1441.  
  1442.                     ai_evaded = 1;
  1443.                     evade_speed = Robot_info[objp->id].evade_speed[Difficulty_level];
  1444.  
  1445.                     move_around_player(objp, vec_to_player, evade_speed);
  1446.                 }
  1447.             }
  1448.             return;
  1449.         }
  1450.     }
  1451.  
  1452.     //  If only allowed to do evade code, then done.
  1453.     //  Hmm, perhaps brilliant insight.  If want claw-type guys to keep coming, don't return here after evasion.
  1454.     if ((!robptr->attack_type) && evade_only)
  1455.         return;
  1456.  
  1457.     //  If we fall out of above, then no object to be avoided.
  1458.     objp->ctype.ai_info.danger_laser_num = -1;
  1459.  
  1460.     //  Green guy selects move around/towards/away based on firing time, not distance.
  1461.     if (robptr->attack_type == 1) {
  1462.         if (((ailp->next_fire > robptr->firing_wait[Difficulty_level]/4) && (dist_to_player < F1_0*30)) || Player_is_dead) {
  1463.             //  1/4 of time, move around player, 3/4 of time, move away from player
  1464.             if (rand() < 8192) {
  1465.                 move_around_player(objp, vec_to_player, -1);
  1466.             } else {
  1467.                 move_away_from_player(objp, vec_to_player, 1);
  1468.             }
  1469.         } else {
  1470.             move_towards_player(objp, vec_to_player);
  1471.         }
  1472.     } else {
  1473.         if (dist_to_player < circle_distance)
  1474.             move_away_from_player(objp, vec_to_player, 0);
  1475.         else if (dist_to_player < circle_distance*2)
  1476.             move_around_player(objp, vec_to_player, -1);
  1477.         else
  1478.             move_towards_player(objp, vec_to_player);
  1479.     }
  1480.  
  1481. }
  1482.  
  1483. // --------------------------------------------------------------------------------------------------------------------
  1484. //  Compute a somewhat random, normalized vector.
  1485. void make_random_vector(vms_vector *vec)
  1486. {
  1487.     vec->x = (rand() - 16384) | 1;  // make sure we don't create null vector
  1488.     vec->y = rand() - 16384;
  1489.     vec->z = rand() - 16384;
  1490.  
  1491.     vm_vec_normalize_quick(vec);
  1492. }
  1493.  
  1494. #ifndef NDEBUG
  1495. void mprintf_animation_info(object *objp)
  1496. {
  1497.     ai_static   *aip = &objp->ctype.ai_info;
  1498.     ai_local        *ailp = &Ai_local_info[objp-Objects];
  1499.  
  1500.     if (!Ai_info_enabled)
  1501.         return;
  1502.  
  1503.     mprintf((0, "Goal = "));
  1504.  
  1505.     switch (aip->GOAL_STATE) {
  1506.         case AIS_NONE:  mprintf((0, "NONE "));  break;
  1507.         case AIS_REST:  mprintf((0, "REST "));  break;
  1508.         case AIS_SRCH:  mprintf((0, "SRCH "));  break;
  1509.         case AIS_LOCK:  mprintf((0, "LOCK "));  break;
  1510.         case AIS_FLIN:  mprintf((0, "FLIN "));  break;
  1511.         case AIS_FIRE:  mprintf((0, "FIRE "));  break;
  1512.         case AIS_RECO:  mprintf((0, "RECO "));  break;
  1513.         case AIS_ERR_:  mprintf((0, "ERR_ "));  break;
  1514.     }
  1515.  
  1516.     mprintf((0, " Cur = "));
  1517.  
  1518.     switch (aip->CURRENT_STATE) {
  1519.         case AIS_NONE:  mprintf((0, "NONE "));  break;
  1520.         case AIS_REST:  mprintf((0, "REST "));  break;
  1521.         case AIS_SRCH:  mprintf((0, "SRCH "));  break;
  1522.         case AIS_LOCK:  mprintf((0, "LOCK "));  break;
  1523.         case AIS_FLIN:  mprintf((0, "FLIN "));  break;
  1524.         case AIS_FIRE:  mprintf((0, "FIRE "));  break;
  1525.         case AIS_RECO:  mprintf((0, "RECO "));  break;
  1526.         case AIS_ERR_:  mprintf((0, "ERR_ "));  break;
  1527.     }
  1528.  
  1529.     mprintf((0, " Aware = "));
  1530.  
  1531.     switch (ailp->player_awareness_type) {
  1532.         case AIE_FIRE: mprintf((0, "FIRE ")); break;
  1533.         case AIE_HITT: mprintf((0, "HITT ")); break;
  1534.         case AIE_COLL: mprintf((0, "COLL ")); break;
  1535.         case AIE_HURT: mprintf((0, "HURT ")); break;
  1536.     }
  1537.  
  1538.     mprintf((0, "Next fire = %6.3f, Time = %6.3f\n", f2fl(ailp->next_fire), f2fl(ailp->player_awareness_time)));
  1539.  
  1540. }
  1541. #endif
  1542.  
  1543. //  -------------------------------------------------------------------------------------------------------------------
  1544. int Break_on_object = -1;
  1545.  
  1546. void do_firing_stuff(object *obj, int player_visibility, vms_vector *vec_to_player)
  1547. {
  1548. //mprintf((0, "!"));
  1549.     if (player_visibility >= 1) {
  1550.         //  Now, if in robot's field of view, lock onto player
  1551.         fix dot = vm_vec_dot(&obj->orient.fvec, vec_to_player);
  1552. //mprintf((0, "dot = %8x ", dot));
  1553.         if ((dot >= 7*F1_0/8) || (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
  1554.             ai_static   *aip = &obj->ctype.ai_info;
  1555.             ai_local        *ailp = &Ai_local_info[obj-Objects];
  1556.  
  1557.             switch (aip->GOAL_STATE) {
  1558.                 case AIS_NONE:
  1559.                 case AIS_REST:
  1560.                 case AIS_SRCH:
  1561.                 case AIS_LOCK:
  1562.                     aip->GOAL_STATE = AIS_FIRE;
  1563.                     if (ailp->player_awareness_type <= PA_NEARBY_ROBOT_FIRED) {
  1564.                         ailp->player_awareness_type = PA_NEARBY_ROBOT_FIRED;
  1565.                         ailp->player_awareness_time = PLAYER_AWARENESS_INITIAL_TIME;
  1566.                     }
  1567.                     break;
  1568.             }
  1569.         } else if (dot >= F1_0/2) {
  1570.             ai_static   *aip = &obj->ctype.ai_info;
  1571.             switch (aip->GOAL_STATE) {
  1572.                 case AIS_NONE:
  1573.                 case AIS_REST:
  1574.                 case AIS_SRCH:
  1575.                     aip->GOAL_STATE = AIS_LOCK;
  1576.                     break;
  1577.             }
  1578.         }
  1579.     }
  1580. }
  1581.  
  1582. // --------------------------------------------------------------------------------------------------------------------
  1583. //  If a hiding robot gets bumped or hit, he decides to find another hiding place.
  1584. void do_ai_robot_hit(object *objp, int type)
  1585. {
  1586.     if (objp->control_type == CT_AI) {
  1587.         if ((type == PA_WEAPON_ROBOT_COLLISION) || (type == PA_PLAYER_COLLISION))
  1588.             switch (objp->ctype.ai_info.behavior) {
  1589.                 case AIM_HIDE:
  1590.                     objp->ctype.ai_info.SUBMODE = AISM_GOHIDE;
  1591.                     break;
  1592.                 case AIM_STILL:
  1593.                     Ai_local_info[objp-Objects].mode = AIM_CHASE_OBJECT;
  1594.                     break;
  1595.             }
  1596.     }
  1597.  
  1598. }
  1599. #ifndef NDEBUG
  1600. int Do_ai_flag=1;
  1601. int Cvv_test=0;
  1602. int Cvv_last_time[MAX_OBJECTS];
  1603. int Gun_point_hack=0;
  1604. #endif
  1605.  
  1606. #define CHASE_TIME_LENGTH       (F1_0*8)
  1607. #define DEFAULT_ROBOT_SOUND_VOLUME      F1_0
  1608. int     Robot_sound_volume=DEFAULT_ROBOT_SOUND_VOLUME;
  1609.  
  1610. // --------------------------------------------------------------------------------------------------------------------
  1611. //  Note: This function could be optimized.  Surely player_is_visible_from_object would benefit from the
  1612. //  information of a normalized vec_to_player.
  1613. //  Return player visibility:
  1614. //      0       not visible
  1615. //      1       visible, but robot not looking at player (ie, on an unobstructed vector)
  1616. //      2       visible and in robot's field of view
  1617. //      -1      player is cloaked
  1618. //  If the player is cloaked, set vec_to_player based on time player cloaked and last uncloaked position.
  1619. //  Updates ailp->previous_visibility if player is not cloaked, in which case the previous visibility is left unchanged
  1620. //  and is copied to player_visibility
  1621. void compute_vis_and_vec(object *objp, vms_vector *pos, ai_local *ailp, vms_vector *vec_to_player, int *player_visibility, robot_info *robptr, int *flag)
  1622. {
  1623.     if (!*flag) {
  1624.         if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
  1625.             fix         delta_time, dist;
  1626.             int         cloak_index = (objp-Objects) % MAX_AI_CLOAK_INFO;
  1627.  
  1628.             delta_time = GameTime - Ai_cloak_info[cloak_index].last_time;
  1629.             if (delta_time > F1_0*2) {
  1630.                 vms_vector  randvec;
  1631.  
  1632.                 Ai_cloak_info[cloak_index].last_time = GameTime;
  1633.                 make_random_vector(&randvec);
  1634.                 vm_vec_scale_add2(&Ai_cloak_info[cloak_index].last_position, &randvec, 8*delta_time );
  1635.             }
  1636.  
  1637.             dist = vm_vec_normalized_dir_quick(vec_to_player, &Ai_cloak_info[cloak_index].last_position, pos);
  1638.             *player_visibility = player_is_visible_from_object(objp, pos, robptr->field_of_view[Difficulty_level], vec_to_player);
  1639.             // *player_visibility = 2;
  1640.  
  1641.             if ((ailp->next_misc_sound_time < GameTime) && (ailp->next_fire < F1_0) && (dist < F1_0*20)) {
  1642.                 mprintf((0, "ANGRY! "));
  1643.                 ailp->next_misc_sound_time = GameTime + (rand() + F1_0) * (7 - Difficulty_level) / 1;
  1644.                 digi_link_sound_to_pos( robptr->see_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
  1645.             }
  1646.         } else {
  1647.             //  Compute expensive stuff -- vec_to_player and player_visibility
  1648.             vm_vec_normalized_dir_quick(vec_to_player, &Believed_player_pos, pos);
  1649.             if ((vec_to_player->x == 0) && (vec_to_player->y == 0) && (vec_to_player->z == 0)) {
  1650.                 mprintf((0, "Warning: Player and robot at exactly the same location.\n"));
  1651.                 vec_to_player->x = F1_0;
  1652.             }
  1653.             *player_visibility = player_is_visible_from_object(objp, pos, robptr->field_of_view[Difficulty_level], vec_to_player);
  1654.  
  1655.             //  This horrible code added by MK in desperation on 12/13/94 to make robots wake up as soon as they
  1656.             //  see you without killing frame rate.
  1657.             {
  1658.                 ai_static   *aip = &objp->ctype.ai_info;
  1659.             if ((*player_visibility == 2) && (ailp->previous_visibility != 2))
  1660.                 if ((aip->GOAL_STATE == AIS_REST) || (aip->CURRENT_STATE == AIS_REST)) {
  1661.                     aip->GOAL_STATE = AIS_FIRE;
  1662.                     aip->CURRENT_STATE = AIS_FIRE;
  1663.                 }
  1664.             }
  1665.  
  1666.             if (!Player_exploded && (ailp->previous_visibility != *player_visibility) && (*player_visibility == 2)) {
  1667.                 if (ailp->previous_visibility == 0) {
  1668.                     if (ailp->time_player_seen + F1_0/2 < GameTime) {
  1669.                         // mprintf((0, "SEE! "));
  1670.                         digi_link_sound_to_pos( robptr->see_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
  1671.                         ailp->time_player_sound_attacked = GameTime;
  1672.                         ailp->next_misc_sound_time = GameTime + F1_0 + rand()*4;
  1673.                     }
  1674.                 } else if (ailp->time_player_sound_attacked + F1_0/4 < GameTime) {
  1675.                     // mprintf((0, "ANGRY! "));
  1676.                     digi_link_sound_to_pos( robptr->attack_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
  1677.                     ailp->time_player_sound_attacked = GameTime;
  1678.                 }
  1679.             } 
  1680.  
  1681.             if ((*player_visibility == 2) && (ailp->next_misc_sound_time < GameTime)) {
  1682.                 // mprintf((0, "ATTACK! "));
  1683.                 ailp->next_misc_sound_time = GameTime + (rand() + F1_0) * (7 - Difficulty_level) / 2;
  1684.                 digi_link_sound_to_pos( robptr->attack_sound, objp->segnum, 0, pos, 0 , Robot_sound_volume);
  1685.             }
  1686.             ailp->previous_visibility = *player_visibility;
  1687.         }
  1688.  
  1689.         *flag = 1;
  1690.  
  1691.         if (*player_visibility) {
  1692.             ailp->time_player_seen = GameTime;
  1693.         }
  1694.     }
  1695.  
  1696. }
  1697.  
  1698. // --------------------------------------------------------------------------------------------------------------------
  1699. //  Move the object objp to a spot in which it doesn't intersect a wall.
  1700. //  It might mean moving it outside its current segment.
  1701. void move_object_to_legal_spot(object *objp)
  1702. {
  1703.     vms_vector  original_pos = objp->pos;
  1704.     int     i;
  1705.     segment *segp = &Segments[objp->segnum];
  1706.  
  1707.     for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
  1708.         if (WALL_IS_DOORWAY(segp, i) & WID_FLY_FLAG) {
  1709.             vms_vector  segment_center, goal_dir;
  1710.             fix         dist_to_center;
  1711.  
  1712.             compute_segment_center(&segment_center, &Segments[segp->children[i]]);
  1713.             vm_vec_sub(&goal_dir, &segment_center, &objp->pos);
  1714.             dist_to_center = vm_vec_normalize_quick(&goal_dir);
  1715.             vm_vec_scale(&goal_dir, objp->size);
  1716.             vm_vec_add2(&objp->pos, &goal_dir);
  1717.             if (!object_intersects_wall(objp)) {
  1718.                 int new_segnum = find_point_seg(&objp->pos, objp->segnum);
  1719.  
  1720.                 if (new_segnum != -1) {
  1721.                     obj_relink(objp-Objects, new_segnum);
  1722.                     return;
  1723.                 }
  1724.             } else
  1725.                 objp->pos = original_pos;
  1726.         }
  1727.     }
  1728.  
  1729.     // Int3();      //  Darn you John, you done it again!  (But contact Mike)
  1730.     mprintf((0, "Note: Killing robot #%i because he's badly stuck outside the mine.\n", objp-Objects));
  1731.  
  1732.     apply_damage_to_robot(objp, objp->shields*2, objp-Objects);
  1733. }
  1734.  
  1735. // --------------------------------------------------------------------------------------------------------------------
  1736. //  Move object one object radii from current position towards segment center.
  1737. //  If segment center is nearer than 2 radii, move it to center.
  1738. void move_towards_segment_center(object *objp)
  1739. {
  1740.     int         segnum = objp->segnum;
  1741.     fix         dist_to_center;
  1742.     vms_vector  segment_center, goal_dir;
  1743.  
  1744.     compute_segment_center(&segment_center, &Segments[segnum]);
  1745.  
  1746.     vm_vec_sub(&goal_dir, &segment_center, &objp->pos);
  1747.     dist_to_center = vm_vec_normalize_quick(&goal_dir);
  1748.  
  1749.     if (dist_to_center < objp->size) {
  1750.         //  Center is nearer than the distance we want to move, so move to center.
  1751.         objp->pos = segment_center;
  1752.         mprintf((0, "Object #%i moved to center of segment #%i (%7.3f %7.3f %7.3f)\n", objp-Objects, objp->segnum, f2fl(objp->pos.x), f2fl(objp->pos.y), f2fl(objp->pos.z)));
  1753.         if (object_intersects_wall(objp)) {
  1754.             mprintf((0, "Object #%i still illegal, trying trickier move.\n"));
  1755.             move_object_to_legal_spot(objp);
  1756.         }
  1757.     } else {
  1758.         int new_segnum;
  1759.         //  Move one radii towards center.
  1760.         vm_vec_scale(&goal_dir, objp->size);
  1761.         vm_vec_add2(&objp->pos, &goal_dir);
  1762.         new_segnum = find_point_seg(&objp->pos, objp->segnum);
  1763.         if (new_segnum == -1) {
  1764.             objp->pos = segment_center;
  1765.             move_object_to_legal_spot(objp);
  1766.         }
  1767.         mprintf((0, "Obj %i moved twrds seg %i (%6.2f %6.2f %6.2f), dists: [%6.2f %6.2f]\n", objp-Objects, objp->segnum, f2fl(objp->pos.x), f2fl(objp->pos.y), f2fl(objp->pos.z), f2fl(vm_vec_dist_quick(&objp->pos, &segment_center)), f2fl(vm_vec_dist_quick(&objp->pos, &segment_center))));
  1768.     }
  1769.  
  1770. }
  1771.  
  1772. //  -----------------------------------------------------------------------------------------------------------
  1773. //  Return true if door can be flown through by a suitable type robot.
  1774. //  Only brains and avoid robots can open doors.
  1775. int ai_door_is_openable(object *objp, segment *segp, int sidenum)
  1776. {
  1777.     int wall_num;
  1778.  
  1779.     //  The mighty console object can open all doors (for purposes of determining paths).
  1780.     if (objp == ConsoleObject) {
  1781.         int wall_num = segp->sides[sidenum].wall_num;
  1782.  
  1783.         if (Walls[wall_num].type == WALL_DOOR)
  1784.             return 1;
  1785.     }
  1786.  
  1787.     if ((objp->id == ROBOT_BRAIN) || (objp->ctype.ai_info.behavior == AIB_RUN_FROM)) {
  1788.         wall_num = segp->sides[sidenum].wall_num;
  1789.  
  1790.         if (wall_num != -1)
  1791.             if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].keys == KEY_NONE) && !(Walls[wall_num].flags & WALL_DOOR_LOCKED))
  1792.                 return 1;
  1793.     }
  1794.  
  1795.     return 0;
  1796. }
  1797.  
  1798. //--//  -----------------------------------------------------------------------------------------------------------
  1799. //--//  Return true if object *objp is allowed to open door at wall_num
  1800. //--int door_openable_by_robot(object *objp, int wall_num)
  1801. //--{
  1802. //--    if (objp->id == ROBOT_BRAIN)
  1803. //--        if (Walls[wall_num].keys == KEY_NONE)
  1804. //--            return 1;
  1805. //--
  1806. //--    return 0;
  1807. //--}
  1808.  
  1809. //  -----------------------------------------------------------------------------------------------------------
  1810. //  Return side of openable door in segment, if any.  If none, return -1.
  1811. int openable_doors_in_segment(object *objp)
  1812. {
  1813.     int i;
  1814.     int segnum = objp->segnum;
  1815.  
  1816.     for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
  1817.         if (Segments[segnum].sides[i].wall_num != -1) {
  1818.             int wall_num = Segments[segnum].sides[i].wall_num;
  1819.             if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].keys == KEY_NONE) && (Walls[wall_num].state == WALL_DOOR_CLOSED) && !(Walls[wall_num].flags & WALL_DOOR_LOCKED))
  1820.                 return i;
  1821.         }
  1822.     }
  1823.  
  1824.     return -1;
  1825.  
  1826. }
  1827.  
  1828. //--unused-- // -----------------------------------------------------------------------------------------------------------
  1829. //--unused-- // For all doors this guy can open in his segment, open them.
  1830. //--unused-- void ai_open_doors_in_segment(object *objp)
  1831. //--unused-- {
  1832. //--unused--    int i;
  1833. //--unused--    int segnum = objp->segnum;
  1834. //--unused-- 
  1835. //--unused--    for (i=0; i<MAX_SIDES_PER_SEGMENT; i++)
  1836. //--unused--        if (Segments[segnum].sides[i].wall_num != -1) {
  1837. //--unused--            int wall_num = Segments[segnum].sides[i].wall_num;
  1838. //--unused--            if ((Walls[wall_num].type == WALL_DOOR) && (Walls[wall_num].keys == KEY_NONE) && (Walls[wall_num].state == WALL_DOOR_CLOSED))
  1839. //--unused--                if (door_openable_by_robot(objp, wall_num)) {
  1840. //--unused--                    mprintf((0, "Trying to open door at segment %i, side %i\n", segnum, i));
  1841. //--unused--                    wall_open_door(&Segments[segnum], i);
  1842. //--unused--                }
  1843. //--unused--        }
  1844. //--unused-- }
  1845.  
  1846. // --------------------------------------------------------------------------------------------------------------------
  1847. //  Return true if a special object (player or control center) is in this segment.
  1848. int special_object_in_seg(int segnum)
  1849. {
  1850.     int objnum;
  1851.  
  1852.     objnum = Segments[segnum].objects;
  1853.  
  1854.     while (objnum != -1) {
  1855.         if ((Objects[objnum].type == OBJ_PLAYER) || (Objects[objnum].type == OBJ_CNTRLCEN)) {
  1856.             mprintf((0, "Special object of type %i in segment %i\n", Objects[objnum].type, segnum));
  1857.             return 1;
  1858.         } else
  1859.             objnum = Objects[objnum].next;
  1860.     }
  1861.  
  1862.     return 0;
  1863. }
  1864.  
  1865. // --------------------------------------------------------------------------------------------------------------------
  1866. //  Randomly select a segment attached to *segp, reachable by flying.
  1867. int get_random_child(int segnum)
  1868. {
  1869.     int sidenum;
  1870.     segment *segp = &Segments[segnum];
  1871.  
  1872.     sidenum = (rand() * 6) >> 15;
  1873.  
  1874.     while (!(WALL_IS_DOORWAY(segp, sidenum) & WID_FLY_FLAG))
  1875.         sidenum = (rand() * 6) >> 15;
  1876.  
  1877.     segnum = segp->children[sidenum];
  1878.  
  1879.     return segnum;
  1880. }
  1881.  
  1882. // --------------------------------------------------------------------------------------------------------------------
  1883. //  Return true if placing an object of size size at pos *pos intersects a (player or robot or control center) in segment *segp.
  1884. int check_object_object_intersection(vms_vector *pos, fix size, segment *segp)
  1885. {
  1886.     int     curobjnum;
  1887.  
  1888.     //  If this would intersect with another object (only check those in this segment), then try to move.
  1889.     curobjnum = segp->objects;
  1890.     while (curobjnum != -1) {
  1891.         object *curobjp = &Objects[curobjnum];
  1892.         if ((curobjp->type == OBJ_PLAYER) || (curobjp->type == OBJ_ROBOT) || (curobjp->type == OBJ_CNTRLCEN)) {
  1893.             if (vm_vec_dist_quick(pos, &curobjp->pos) < size + curobjp->size)
  1894.                 return 1;
  1895.         }
  1896.         curobjnum = curobjp->next;
  1897.     }
  1898.  
  1899.     return 0;
  1900.  
  1901. }
  1902.  
  1903. #ifndef SHAREWARE
  1904.  
  1905. // --------------------------------------------------------------------------------------------------------------------
  1906. //  Return true if object created, else return false.
  1907. int create_gated_robot( int segnum, int object_id)
  1908. {
  1909.     int     objnum;
  1910.     object  *objp;
  1911.     segment *segp = &Segments[segnum];
  1912.     vms_vector  object_pos;
  1913.     robot_info  *robptr = &Robot_info[object_id];
  1914.     int     i, count=0;
  1915.     fix     objsize = Polygon_models[robptr->model_num].rad;
  1916.     int     default_behavior;
  1917.  
  1918.     for (i=0; i<=Highest_object_index; i++)
  1919.         if (Objects[i].type == OBJ_ROBOT)
  1920.             if (Objects[i].matcen_creator == BOSS_GATE_MATCEN_NUM)
  1921.                 count++;
  1922.  
  1923.     if (count > 2*Difficulty_level + 3) {
  1924.         // mprintf((0, "Cannot gate in a robot until you kill one.\n"));
  1925.         Last_gate_time = GameTime - 3*Gate_interval/4;
  1926.         return 0;
  1927.     }
  1928.  
  1929.     compute_segment_center(&object_pos, segp);
  1930.     pick_random_point_in_seg(&object_pos, segp-Segments);
  1931.  
  1932.     //  See if legal to place object here.  If not, move about in segment and try again.
  1933.     if (check_object_object_intersection(&object_pos, objsize, segp)) {
  1934.         // mprintf((0, "Can't get in because object collides with something.\n"));
  1935.         Last_gate_time = GameTime - 3*Gate_interval/4;
  1936.         return 0;
  1937.     }
  1938.  
  1939.     objnum = obj_create(OBJ_ROBOT, object_id, segnum, &object_pos, &vmd_identity_matrix, objsize, CT_AI, MT_PHYSICS, RT_POLYOBJ);
  1940.  
  1941.     if ( objnum < 0 ) {
  1942.         // mprintf((1, "Can't get object to gate in robot.  Not gating in.\n"));
  1943.         Last_gate_time = GameTime - 3*Gate_interval/4;
  1944.         return 0;
  1945.     }
  1946.  
  1947.     mprintf((0, "Gating in object %i in segment %i\n", objnum, segp-Segments));
  1948.  
  1949.     #ifdef NETWORK
  1950.     Net_create_objnums[0] = objnum; // A convenient global to get objnum back to caller for multiplayer
  1951.     #endif
  1952.  
  1953.     objp = &Objects[objnum];
  1954.  
  1955.     //Set polygon-object-specific data
  1956.  
  1957.     objp->rtype.pobj_info.model_num = robptr->model_num;
  1958.     objp->rtype.pobj_info.subobj_flags = 0;
  1959.  
  1960.     //set Physics info
  1961.  
  1962.     objp->mtype.phys_info.mass = robptr->mass;
  1963.     objp->mtype.phys_info.drag = robptr->drag;
  1964.  
  1965.     objp->mtype.phys_info.flags |= (PF_LEVELLING);
  1966.  
  1967.     objp->shields = robptr->strength;
  1968.     objp->matcen_creator = BOSS_GATE_MATCEN_NUM;    //  flag this robot as having been created by the boss.
  1969.  
  1970.     default_behavior = AIB_NORMAL;
  1971.     if (object_id == 10)                        //  This is a toaster guy!
  1972.         default_behavior = AIB_RUN_FROM;
  1973.  
  1974.     init_ai_object(objp-Objects, default_behavior, -1 );        //  Note, -1 = segment this robot goes to to hide, should probably be something useful
  1975.  
  1976.     object_create_explosion(segnum, &object_pos, i2f(10), VCLIP_MORPHING_ROBOT );
  1977.     digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, segnum, 0, &object_pos, 0 , F1_0);
  1978.     morph_start(objp);
  1979.  
  1980.     Last_gate_time = GameTime;
  1981.  
  1982.     Players[Player_num].num_robots_level++;
  1983.     Players[Player_num].num_robots_total++;
  1984.  
  1985.     return 1;
  1986. }
  1987.  
  1988. // --------------------------------------------------------------------------------------------------------------------
  1989. //  Make object objp gate in a robot.
  1990. //  The process of him bringing in a robot takes one second.
  1991. //  Then a robot appears somewhere near the player.
  1992. //  Return true if robot successfully created, else return false
  1993. int gate_in_robot(int type, int segnum)
  1994. {
  1995.     if (segnum < 0)
  1996.         segnum = Boss_gate_segs[(rand() * Num_boss_gate_segs) >> 15];
  1997.  
  1998.     Assert((segnum >= 0) && (segnum <= Highest_segment_index));
  1999.  
  2000.     return create_gated_robot(segnum, type);
  2001. }
  2002.  
  2003. #endif
  2004.  
  2005. //// --------------------------------------------------------------------------------------------------------------------
  2006. ////    Return true if some distance function of vertices implies segment is too small for object.
  2007. //int segment_too_small_for_object(object *objp, segment *segp)
  2008. //{
  2009. //  int i;
  2010. //  fix threshold_distance;
  2011. //
  2012. //  threshold_distance = objp->size*2;
  2013. //
  2014. //  for (i=1; i<MAX_VERTICES_PER_SEGMENT; i++)
  2015. //      if (vm_vec_dist_quick(&Vertices[segp->verts[i]], &Vertices[segp->verts[i-1]]) < threshold_distance) {
  2016. //#ifndef NDEBUG
  2017. //          fix dist = vm_vec_dist_quick(&Vertices[segp->verts[i]], &Vertices[segp->verts[i-1]]);
  2018. //#endif
  2019. //          mprintf((0, "Seg %i too small for obj %i (sz=%7.3f), verts %i, %i only %7.3f apart\n", segp-Segments, objp-Objects, f2fl(objp->size), segp->verts[i], segp->verts[i-1], f2fl(dist)));
  2020. //          return 1;
  2021. //      }
  2022. //
  2023. //  return 0;
  2024. //}
  2025.  
  2026. //--unused-- int    Shown_all_segments=0;
  2027.  
  2028. // --------------------------------------------------------------------------------------------------------------------
  2029. int boss_fits_in_seg(object *boss_objp, int segnum)
  2030. {
  2031.     vms_vector  segcenter;
  2032.     int         boss_objnum = boss_objp-Objects;
  2033.     int         posnum;
  2034.  
  2035.     compute_segment_center(&segcenter, &Segments[segnum]);
  2036.  
  2037.     for (posnum=0; posnum<9; posnum++) {
  2038.         if (posnum > 0) {
  2039.             vms_vector  vertex_pos;
  2040.  
  2041.             Assert((posnum-1 >= 0) && (posnum-1 < 8));
  2042.             vertex_pos = Vertices[Segments[segnum].verts[posnum-1]];
  2043.             vm_vec_avg(&boss_objp->pos, &vertex_pos, &segcenter);
  2044.         } else
  2045.             boss_objp->pos = segcenter;
  2046.  
  2047.         obj_relink(boss_objnum, segnum);
  2048.         if (!object_intersects_wall(boss_objp))
  2049.             return 1;
  2050.     }
  2051.  
  2052.     return 0;
  2053. }
  2054.  
  2055. #define QUEUE_SIZE  256
  2056.  
  2057. // --------------------------------------------------------------------------------------------------------------------
  2058. //  Create list of segments boss is allowed to teleport to at segptr.
  2059. //  Set *num_segs.
  2060. //  Boss is allowed to teleport to segments he fits in (calls object_intersects_wall) and
  2061. //  he can reach from his initial position (calls find_connected_distance).
  2062. //  If size_check is set, then only add segment if boss can fit in it, else any segment is legal.
  2063. void init_boss_segments(short segptr[], int *num_segs, int size_check)
  2064. {
  2065.     int         boss_objnum=-1;
  2066.     int         i;
  2067.  
  2068.     *num_segs = 0;
  2069. #ifdef EDITOR
  2070.     N_selected_segs = 0;
  2071. #endif
  2072.  
  2073.  
  2074.     //  See if there is a boss.  If not, quick out.
  2075.     for (i=0; i<=Highest_object_index; i++)
  2076.         if ((Objects[i].type == OBJ_ROBOT) && (Robot_info[Objects[i].id].boss_flag)) {
  2077.             Assert(boss_objnum == -1);      //  There are two bosses in this mine!  i and boss_objnum!
  2078.             boss_objnum = i;
  2079.         }
  2080.  
  2081.     if (boss_objnum != -1) {
  2082.         int         original_boss_seg;
  2083.         vms_vector  original_boss_pos;
  2084.         object      *boss_objp = &Objects[boss_objnum];
  2085.         int         head, tail;
  2086.         int         seg_queue[QUEUE_SIZE];
  2087. //ALREADY IN RENDER.H       byte            visited[MAX_SEGMENTS];
  2088.         fix         boss_size_save;
  2089.  
  2090.         boss_size_save = boss_objp->size;
  2091.         boss_objp->size = fixmul((F1_0/4)*3, boss_objp->size);
  2092.         original_boss_seg = boss_objp->segnum;
  2093.         original_boss_pos = boss_objp->pos;
  2094.         head = 0;
  2095.         tail = 0;
  2096.         seg_queue[head++] = original_boss_seg;
  2097.  
  2098.         segptr[(*num_segs)++] = original_boss_seg;
  2099.         #ifdef EDITOR
  2100.         Selected_segs[N_selected_segs++] = original_boss_seg;
  2101.         #endif
  2102.  
  2103.         for (i=0; i<=Highest_segment_index; i++)
  2104.             visited[i] = 0;
  2105.  
  2106.         while (tail != head) {
  2107.             int     sidenum;
  2108.             segment *segp = &Segments[seg_queue[tail++]];
  2109.  
  2110.             tail &= QUEUE_SIZE-1;
  2111.  
  2112.             for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
  2113.                 if (WALL_IS_DOORWAY(segp, sidenum) & WID_FLY_FLAG) {
  2114.                     if (visited[segp->children[sidenum]] == 0) {
  2115.                         seg_queue[head++] = segp->children[sidenum];
  2116.                         visited[segp->children[sidenum]] = 1;
  2117.                         head &= QUEUE_SIZE-1;
  2118.                         if (head > tail) {
  2119.                             if (head == tail + QUEUE_SIZE-1)
  2120.                                 Int3(); //  queue overflow.  Make it bigger!
  2121.                         } else
  2122.                             if (head+QUEUE_SIZE == tail + QUEUE_SIZE-1)
  2123.                                 Int3(); //  queue overflow.  Make it bigger!
  2124.     
  2125.                         if ((!size_check) || boss_fits_in_seg(boss_objp, segp->children[sidenum])) {
  2126.                             segptr[(*num_segs)++] = segp->children[sidenum];
  2127.                             #ifdef EDITOR
  2128.                             Selected_segs[N_selected_segs++] = segp->children[sidenum];
  2129.                             #endif
  2130.                             if (*num_segs >= MAX_BOSS_TELEPORT_SEGS) {
  2131.                                 mprintf((1, "Warning: Too many boss teleport segments.  Found %i after searching %i/%i segments.\n", MAX_BOSS_TELEPORT_SEGS, segp->children[sidenum], Highest_segment_index+1));
  2132.                                 tail = head;
  2133.                             }
  2134.                         }
  2135.                     }
  2136.                 }
  2137.             }
  2138.  
  2139.         }
  2140.  
  2141.         boss_objp->size = boss_size_save;
  2142.         boss_objp->pos = original_boss_pos;
  2143.         obj_relink(boss_objnum, original_boss_seg);
  2144.  
  2145.     }
  2146.  
  2147. }
  2148.  
  2149. // --------------------------------------------------------------------------------------------------------------------
  2150. void teleport_boss(object *objp)
  2151. {
  2152.     int         rand_segnum;
  2153.     vms_vector  boss_dir;
  2154.     int         rand_seg;
  2155.     Assert(Num_boss_teleport_segs > 0);
  2156.  
  2157.     //  Pick a random segment from the list of boss-teleportable-to segments.
  2158.     rand_seg = (rand() * Num_boss_teleport_segs) >> 15; 
  2159.     rand_segnum = Boss_teleport_segs[rand_seg];
  2160.     Assert((rand_segnum >= 0) && (rand_segnum <= Highest_segment_index));
  2161.  
  2162. #ifndef SHAREWARE
  2163. #ifdef NETWORK
  2164.     if (Game_mode & GM_MULTI)
  2165.         multi_send_boss_actions(objp-Objects, 1, rand_seg, 0);
  2166. #endif
  2167. #endif
  2168.  
  2169.     compute_segment_center(&objp->pos, &Segments[rand_segnum]);
  2170.     obj_relink(objp-Objects, rand_segnum);
  2171.  
  2172.     Last_teleport_time = GameTime;
  2173.  
  2174.     //  make boss point right at player
  2175.     vm_vec_sub(&boss_dir, &Objects[Players[Player_num].objnum].pos, &objp->pos);
  2176.     vm_vector_2_matrix(&objp->orient, &boss_dir, NULL, NULL);
  2177.  
  2178.     digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, rand_segnum, 0, &objp->pos, 0 , F1_0);
  2179.     digi_kill_sound_linked_to_object( objp-Objects);
  2180.     digi_link_sound_to_object2( SOUND_BOSS_SHARE_SEE, objp-Objects, 1, F1_0, F1_0*512 );    //  F1_0*512 means play twice as loud
  2181.     #ifndef NDEBUG
  2182.     mprintf((0, "Boss teleported to segment %i\n", rand_segnum));
  2183.     #endif
  2184.  
  2185.     //  After a teleport, boss can fire right away.
  2186.     Ai_local_info[objp-Objects].next_fire = 0;
  2187.  
  2188. }
  2189.  
  2190. //  ----------------------------------------------------------------------
  2191. void start_boss_death_sequence(object *objp)
  2192. {
  2193.     if (Robot_info[objp->id].boss_flag) {
  2194.         Boss_dying = 1;
  2195.         Boss_dying_start_time = GameTime;
  2196.     }
  2197.  
  2198. }
  2199.  
  2200. //  ----------------------------------------------------------------------
  2201. void do_boss_dying_frame(object *objp)
  2202. {
  2203.     fix boss_roll_val, temp;
  2204.  
  2205.     boss_roll_val = fixdiv(GameTime - Boss_dying_start_time, BOSS_DEATH_DURATION);
  2206.  
  2207.     fix_sincos(fixmul(boss_roll_val, boss_roll_val), &temp, &objp->mtype.phys_info.rotvel.x);
  2208.     fix_sincos(boss_roll_val, &temp, &objp->mtype.phys_info.rotvel.y);
  2209.     fix_sincos(boss_roll_val-F1_0/8, &temp, &objp->mtype.phys_info.rotvel.z);
  2210.  
  2211.     objp->mtype.phys_info.rotvel.x = (GameTime - Boss_dying_start_time)/9;
  2212.     objp->mtype.phys_info.rotvel.y = (GameTime - Boss_dying_start_time)/5;
  2213.     objp->mtype.phys_info.rotvel.z = (GameTime - Boss_dying_start_time)/7;
  2214.  
  2215.     if (Boss_dying_start_time + BOSS_DEATH_DURATION - BOSS_DEATH_SOUND_DURATION < GameTime) {
  2216.         if (!Boss_dying_sound_playing) {
  2217.             mprintf((0, "Starting boss death sound!\n"));
  2218.             Boss_dying_sound_playing = 1;
  2219.             digi_link_sound_to_object2( SOUND_BOSS_SHARE_DIE, objp-Objects, 0, F1_0*4, F1_0*1024 ); //  F1_0*512 means play twice as loud
  2220.         } else if (rand() < FrameTime*16)
  2221.             create_small_fireball_on_object(objp, (F1_0 + rand()) * 8, 0);
  2222.     } else if (rand() < FrameTime*8)
  2223.         create_small_fireball_on_object(objp, (F1_0/2 + rand()) * 8, 1);
  2224.  
  2225.     if (Boss_dying_start_time + BOSS_DEATH_DURATION < GameTime) {
  2226.         do_controlcen_destroyed_stuff(NULL);
  2227.         explode_object(objp, F1_0/4);
  2228.         digi_link_sound_to_object2(SOUND_BADASS_EXPLOSION, objp-Objects, 0, F2_0, F1_0*512);
  2229.     }
  2230. }
  2231.  
  2232. #ifndef SHAREWARE 
  2233. #ifdef NETWORK
  2234. // --------------------------------------------------------------------------------------------------------------------
  2235. //  Called for an AI object if it is fairly aware of the player.
  2236. //  awareness_level is in 0..100.  Larger numbers indicate greater awareness (eg, 99 if firing at player).
  2237. //  In a given frame, might not get called for an object, or might be called more than once.
  2238. //  The fact that this routine is not called for a given object does not mean that object is not interested in the player.
  2239. //  Objects are moved by physics, so they can move even if not interested in a player.  However, if their velocity or
  2240. //  orientation is changing, this routine will be called.
  2241. //  Return value:
  2242. //      0   this player IS NOT allowed to move this robot.
  2243. //      1   this player IS allowed to move this robot.
  2244. int ai_multiplayer_awareness(object *objp, int awareness_level)
  2245. {
  2246.     int rval=1;
  2247.  
  2248. #ifndef SHAREWARE
  2249.     if (Game_mode & GM_MULTI) {
  2250.         if (awareness_level == 0)
  2251.             return 0;
  2252.         rval = multi_can_move_robot(objp-Objects, awareness_level);
  2253.     }
  2254. #endif
  2255.  
  2256.     return rval;
  2257.  
  2258. }
  2259. #else
  2260. #define ai_multiplayer_awareness(a, b) 1
  2261. #endif
  2262. #else
  2263. #define ai_multiplayer_awareness(a, b) 1
  2264. #endif
  2265.  
  2266. #ifndef NDEBUG
  2267. fix Prev_boss_shields = -1;
  2268. #endif
  2269.  
  2270. // --------------------------------------------------------------------------------------------------------------------
  2271. //  Do special stuff for a boss.
  2272. void do_boss_stuff(object *objp)
  2273. {
  2274.     //  New code, fixes stupid bug which meant boss never gated in robots if > 32767 seconds played.
  2275.     if (Last_teleport_time > GameTime)
  2276.         Last_teleport_time = GameTime;
  2277.  
  2278.     if (Last_gate_time > GameTime)
  2279.         Last_gate_time = GameTime;
  2280.  
  2281. #ifndef NDEBUG
  2282.     if (objp->shields != Prev_boss_shields) {
  2283.         mprintf((0, "Boss shields = %7.3f, object %i\n", f2fl(objp->shields), objp-Objects));
  2284.         Prev_boss_shields = objp->shields;
  2285.     }
  2286. #endif
  2287.  
  2288.     if (!Boss_dying) {
  2289.         if (objp->ctype.ai_info.CLOAKED == 1) {
  2290.             if ((GameTime - Boss_cloak_start_time > BOSS_CLOAK_DURATION/3) && (Boss_cloak_end_time - GameTime > BOSS_CLOAK_DURATION/3) && (GameTime - Last_teleport_time > Boss_teleport_interval)) {
  2291.                 if (ai_multiplayer_awareness(objp, 98))
  2292.                     teleport_boss(objp);
  2293.             } else if (Boss_hit_this_frame) {
  2294.                 Boss_hit_this_frame = 0;
  2295.                 Last_teleport_time -= Boss_teleport_interval/4;
  2296.             }
  2297.  
  2298.             if (GameTime > Boss_cloak_end_time)
  2299.                 objp->ctype.ai_info.CLOAKED = 0;
  2300.         } else {
  2301.             if ((GameTime - Boss_cloak_end_time > Boss_cloak_interval) || Boss_hit_this_frame) {
  2302.                 if (ai_multiplayer_awareness(objp, 95))
  2303.                 {
  2304.                     Boss_hit_this_frame = 0;
  2305.                     Boss_cloak_start_time = GameTime;
  2306.                     Boss_cloak_end_time = GameTime+Boss_cloak_duration;
  2307.                     objp->ctype.ai_info.CLOAKED = 1;
  2308. #ifndef SHAREWARE
  2309. #ifdef NETWORK
  2310.                     if (Game_mode & GM_MULTI)
  2311.                         multi_send_boss_actions(objp-Objects, 2, 0, 0);
  2312. #endif
  2313. #endif
  2314.                 }
  2315.             }
  2316.         }
  2317.     } else
  2318.         do_boss_dying_frame(objp);
  2319.  
  2320. }
  2321.  
  2322. #define BOSS_TO_PLAYER_GATE_DISTANCE    (F1_0*150)
  2323.  
  2324. #ifndef SHAREWARE
  2325.  
  2326. // --------------------------------------------------------------------------------------------------------------------
  2327. //  Do special stuff for a boss.
  2328. void do_super_boss_stuff(object *objp, fix dist_to_player, int player_visibility)
  2329. {
  2330.     static int eclip_state = 0;
  2331.     do_boss_stuff(objp);
  2332.  
  2333.     // Only master player can cause gating to occur.
  2334.     #ifdef NETWORK
  2335.     if ((Game_mode & GM_MULTI) && !network_i_am_master())
  2336.         return; 
  2337.     #endif
  2338.  
  2339.     if ((dist_to_player < BOSS_TO_PLAYER_GATE_DISTANCE) || player_visibility || (Game_mode & GM_MULTI)) {
  2340.         if (GameTime - Last_gate_time > Gate_interval/2) {
  2341.             restart_effect(BOSS_ECLIP_NUM);
  2342. #ifndef SHAREWARE
  2343. #ifdef NETWORK
  2344.             if (eclip_state == 0) {
  2345.                 multi_send_boss_actions(objp-Objects, 4, 0, 0);
  2346.                 eclip_state = 1;
  2347.             }
  2348. #endif
  2349. #endif
  2350.         }
  2351.         else {
  2352.             stop_effect(BOSS_ECLIP_NUM);
  2353. #ifndef SHAREWARE
  2354. #ifdef NETWORK
  2355.             if (eclip_state == 1) {
  2356.                 multi_send_boss_actions(objp-Objects, 5, 0, 0);
  2357.                 eclip_state = 0;
  2358.             }
  2359. #endif
  2360. #endif
  2361.         }
  2362.  
  2363.         if (GameTime - Last_gate_time > Gate_interval)
  2364.             if (ai_multiplayer_awareness(objp, 99)) {
  2365.                 int rtval;
  2366.                 int randtype = (rand() * MAX_GATE_INDEX) >> 15;
  2367.  
  2368.                 Assert(randtype < MAX_GATE_INDEX);
  2369.                 randtype = Super_boss_gate_list[randtype];
  2370.                 Assert(randtype < N_robot_types);
  2371.  
  2372.                 rtval = gate_in_robot(randtype, -1);
  2373. #ifndef SHAREWARE
  2374. #ifdef NETWORK
  2375.                 if (rtval && (Game_mode & GM_MULTI))
  2376.                 {
  2377.                     multi_send_boss_actions(objp-Objects, 3, randtype, Net_create_objnums[0]);
  2378.                     map_objnum_local_to_local(Net_create_objnums[0]);
  2379.                 }
  2380. #endif
  2381. #endif
  2382.             }   
  2383.     }
  2384. }
  2385.  
  2386. #endif
  2387.  
  2388. //int multi_can_move_robot(object *objp, int awareness_level)
  2389. //{
  2390. //  return 0;
  2391. //}
  2392.  
  2393. #ifndef SHAREWARE
  2394. void ai_multi_send_robot_position(int objnum, int force)
  2395. {
  2396. #ifndef SHAREWARE
  2397. #ifdef NETWORK
  2398.     if (Game_mode & GM_MULTI) 
  2399.     {
  2400.         if (force != -1)
  2401.             multi_send_robot_position(objnum, 1);
  2402.         else
  2403.             multi_send_robot_position(objnum, 0);
  2404.     }
  2405. #endif
  2406. #endif
  2407.     return;
  2408. }
  2409. #else
  2410. #define ai_multi_send_robot_position(a, b)
  2411. #endif
  2412.  
  2413. // --------------------------------------------------------------------------------------------------------------------
  2414. //  Returns true if this object should be allowed to fire at the player.
  2415. int maybe_ai_do_actual_firing_stuff(object *obj, ai_static *aip)
  2416. {
  2417.     if (Game_mode & GM_MULTI)
  2418.         if ((aip->GOAL_STATE != AIS_FLIN) && (obj->id != ROBOT_BRAIN))
  2419.             if (aip->CURRENT_STATE == AIS_FIRE)
  2420.                 return 1;
  2421.  
  2422.     return 0;
  2423. }
  2424.  
  2425. // --------------------------------------------------------------------------------------------------------------------
  2426. void ai_do_actual_firing_stuff(object *obj, ai_static *aip, ai_local *ailp, robot_info *robptr, vms_vector *vec_to_player, fix dist_to_player, vms_vector *gun_point, int player_visibility, int object_animates)
  2427. {
  2428.     fix dot;
  2429.  
  2430.     if (player_visibility == 2) {
  2431.         //  Changed by mk, 01/04/94, onearm would take about 9 seconds until he can fire at you.
  2432.         // if (((!object_animates) || (ailp->achieved_state[aip->CURRENT_GUN] == AIS_FIRE)) && (ailp->next_fire <= 0)) {
  2433.         if (!object_animates || (ailp->next_fire <= 0)) {
  2434.             dot = vm_vec_dot(&obj->orient.fvec, vec_to_player);
  2435.             if (dot >= 7*F1_0/8) {
  2436.  
  2437.                 if (aip->CURRENT_GUN < Robot_info[obj->id].n_guns) {
  2438.                     if (robptr->attack_type == 1) {
  2439.                         if (!Player_exploded && (dist_to_player < obj->size + ConsoleObject->size + F1_0*2)) {      // robptr->circle_distance[Difficulty_level] + ConsoleObject->size) {
  2440.                             if (!ai_multiplayer_awareness(obj, ROBOT_FIRE_AGITATION-2))
  2441.                                 return;
  2442.                             do_ai_robot_hit_attack(obj, ConsoleObject, &obj->pos);
  2443.                         } else {
  2444.                             // mprintf((0, "Green won't fire: Too far: dist = %7.3f, threshold = %7.3f\n", f2fl(dist_to_player), f2fl(obj->size + ConsoleObject->size + F1_0*2)));
  2445.                             return;
  2446.                         }
  2447.                     } else {
  2448.                         if ((gun_point->x == 0) && (gun_point->y == 0) && (gun_point->z == 0)) {
  2449.                             ; //mprintf((0, "Would like to fire gun, but gun not selected.\n"));
  2450.                         } else {
  2451.                             if (!ai_multiplayer_awareness(obj, ROBOT_FIRE_AGITATION))
  2452.                                 return;
  2453.                             ai_fire_laser_at_player(obj, gun_point);
  2454.                         }
  2455.                     }
  2456.  
  2457.                     //  Wants to fire, so should go into chase mode, probably.
  2458.                     if ( (aip->behavior != AIB_RUN_FROM) && (aip->behavior != AIB_STILL) && (aip->behavior != AIB_FOLLOW_PATH) && ((ailp->mode == AIM_FOLLOW_PATH) || (ailp->mode == AIM_STILL)))
  2459.                         ailp->mode = AIM_CHASE_OBJECT;
  2460.                 }
  2461.  
  2462.                 aip->GOAL_STATE = AIS_RECO;
  2463.                 ailp->goal_state[aip->CURRENT_GUN] = AIS_RECO;
  2464.  
  2465.                 // Switch to next gun for next fire.
  2466.                 aip->CURRENT_GUN++;
  2467.                 if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
  2468.                     aip->CURRENT_GUN = 0;
  2469.             }
  2470.         }
  2471.     } else if (Weapon_info[Robot_info[obj->id].weapon_type].homing_flag == 1) {
  2472.         //  Robots which fire homing weapons might fire even if they don't have a bead on the player.
  2473.         if (((!object_animates) || (ailp->achieved_state[aip->CURRENT_GUN] == AIS_FIRE)) && (ailp->next_fire <= 0) && (vm_vec_dist_quick(&Hit_pos, &obj->pos) > F1_0*40)) {
  2474.             if (!ai_multiplayer_awareness(obj, ROBOT_FIRE_AGITATION))
  2475.                 return;
  2476.             ai_fire_laser_at_player(obj, gun_point);
  2477.  
  2478.             aip->GOAL_STATE = AIS_RECO;
  2479.             ailp->goal_state[aip->CURRENT_GUN] = AIS_RECO;
  2480.  
  2481.             // Switch to next gun for next fire.
  2482.             aip->CURRENT_GUN++;
  2483.             if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
  2484.                 aip->CURRENT_GUN = 0;
  2485.         } else {
  2486.             // Switch to next gun for next fire.
  2487.             aip->CURRENT_GUN++;
  2488.             if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
  2489.                 aip->CURRENT_GUN = 0;
  2490.         }
  2491.     }
  2492. }
  2493.  
  2494. // --------------------------------------------------------------------------------------------------------------------
  2495. void do_ai_frame(object *obj)
  2496. {
  2497.     int         objnum = obj-Objects;
  2498.     ai_static   *aip = &obj->ctype.ai_info;
  2499.     ai_local        *ailp = &Ai_local_info[objnum];
  2500.     fix         dist_to_player;
  2501.     vms_vector  vec_to_player;
  2502.     fix         dot;
  2503.     robot_info  *robptr;
  2504.     int         player_visibility=-1;
  2505.     int         obj_ref;
  2506.     int         object_animates;
  2507.     int         new_goal_state;
  2508.     int         visibility_and_vec_computed = 0;
  2509.     int         previous_visibility;
  2510.     vms_vector  gun_point;
  2511.     vms_vector  vis_vec_pos;
  2512.  
  2513.     if (aip->SKIP_AI_COUNT) {
  2514.         aip->SKIP_AI_COUNT--;
  2515.         return;
  2516.     }
  2517.  
  2518.     //  Kind of a hack.  If a robot is flinching, but it is time for it to fire, unflinch it.
  2519.     //  Else, you can turn a big nasty robot into a wimp by firing flares at it.
  2520.     //  This also allows the player to see the cool flinch effect for mechs without unbalancing the game.
  2521.     if ((aip->GOAL_STATE == AIS_FLIN) && (ailp->next_fire < 0)) {
  2522.         aip->GOAL_STATE = AIS_FIRE;
  2523.     }
  2524.  
  2525. #ifndef NDEBUG
  2526.     if ((aip->behavior == AIB_RUN_FROM) && (ailp->mode != AIM_RUN_FROM_OBJECT))
  2527.         Int3(); //  This is peculiar.  Behavior is run from, but mode is not.  Contact Mike.
  2528.  
  2529.     mprintf_animation_info((obj));
  2530.  
  2531.     if (Ai_animation_test) {
  2532.         if (aip->GOAL_STATE == aip->CURRENT_STATE) {
  2533.             aip->GOAL_STATE++;
  2534.             if (aip->GOAL_STATE > AIS_RECO)
  2535.                 aip->GOAL_STATE = AIS_REST;
  2536.         }
  2537.  
  2538.         mprintf((0, "Frame %4i, current = %i, goal = %i\n", FrameCount, aip->CURRENT_STATE, aip->GOAL_STATE));
  2539.         object_animates = do_silly_animation(obj);
  2540.         if (object_animates)
  2541.             ai_frame_animation(obj);
  2542.         return;
  2543.     }
  2544.  
  2545.     if (!Do_ai_flag)
  2546.         return;
  2547.  
  2548.     if (Break_on_object != -1)
  2549.         if ((obj-Objects) == Break_on_object)
  2550.             Int3(); //  Contact Mike: This is a debug break
  2551. #endif
  2552.  
  2553.     Believed_player_pos = Ai_cloak_info[objnum & (MAX_AI_CLOAK_INFO-1)].last_position;
  2554.  
  2555.     // mprintf((0, "Object %i: behavior = %02x, mode = %i, awareness = %i, time = %7.3f\n", obj-Objects, aip->behavior, ailp->mode, ailp->player_awareness_type, f2fl(ailp->player_awareness_time)));
  2556.     // mprintf((0, "Object %i: behavior = %02x, mode = %i, awareness = %i, cur=%i, goal=%i\n", obj-Objects, aip->behavior, ailp->mode, ailp->player_awareness_type, aip->CURRENT_STATE, aip->GOAL_STATE));
  2557.  
  2558. //  Assert((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR));
  2559.     if (!((aip->behavior >= MIN_BEHAVIOR) && (aip->behavior <= MAX_BEHAVIOR))) {
  2560.         // mprintf((0, "Object %i behavior is %i, setting to AIB_NORMAL, fix in editor!\n", objnum, aip->behavior));
  2561.         aip->behavior = AIB_NORMAL;
  2562.     }
  2563.  
  2564.     Assert(obj->segnum != -1);
  2565.     Assert(obj->id < N_robot_types);
  2566.  
  2567.     robptr = &Robot_info[obj->id];
  2568.     Assert(robptr->always_0xabcd == 0xabcd);
  2569.     obj_ref = objnum ^ FrameCount;
  2570.     // -- if (ailp->wait_time > -F1_0*8)
  2571.     // --   ailp->wait_time -= FrameTime;
  2572.     if (ailp->next_fire > -F1_0*8)
  2573.         ailp->next_fire -= FrameTime;
  2574.     if (ailp->time_since_processed < F1_0*256)
  2575.         ailp->time_since_processed += FrameTime;
  2576.     previous_visibility = ailp->previous_visibility;    //  Must get this before we toast the master copy!
  2577.  
  2578.     //  Deal with cloaking for robots which are cloaked except just before firing.
  2579.     if (robptr->cloak_type == RI_CLOAKED_EXCEPT_FIRING)
  2580.         if (ailp->next_fire < F1_0/2)
  2581.             aip->CLOAKED = 1;
  2582.         else
  2583.             aip->CLOAKED = 0;
  2584.  
  2585.     if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
  2586.         Believed_player_pos = ConsoleObject->pos;
  2587.  
  2588.     dist_to_player = vm_vec_dist_quick(&Believed_player_pos, &obj->pos);
  2589.  
  2590. //--!--     mprintf((0, "%2i: %s, [vel = %5.1f %5.1f %5.1f] spd = %4.1f dtp=%5.1f ", objnum, mode_text[ailp->mode], f2fl(obj->mtype.phys_info.velocity.x), f2fl(obj->mtype.phys_info.velocity.y), f2fl(obj->mtype.phys_info.velocity.z), f2fl(vm_vec_mag(&obj->mtype.phys_info.velocity)), f2fl(dist_to_player)));
  2591. //--!--     if (ailp->mode == AIM_FOLLOW_PATH) {
  2592. //--!--         mprintf((0, "gseg = %i\n", Point_segs[aip->hide_index+aip->cur_path_index].segnum));
  2593. //--!--     } else
  2594. //--!--         mprintf((0, "\n"));
  2595.  
  2596.     //  If this robot can fire, compute visibility from gun position.
  2597.     //  Don't want to compute visibility twice, as it is expensive.  (So is call to calc_gun_point).
  2598.     if ((ailp->next_fire <= 0) && (dist_to_player < F1_0*200) && (robptr->n_guns) && !(robptr->attack_type)) {
  2599.         calc_gun_point(&gun_point, obj, aip->CURRENT_GUN);
  2600.         vis_vec_pos = gun_point;
  2601.         // mprintf((0, "Visibility = %i, computed from gun #%i\n", player_visibility, aip->CURRENT_GUN));
  2602.     } else {
  2603.         vis_vec_pos = obj->pos;
  2604.         vm_vec_zero(&gun_point);
  2605.         // mprintf((0, "Visibility = %i, computed from center.\n", player_visibility));
  2606.     }
  2607.  
  2608.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2609.     //  Occasionally make non-still robots make a path to the player.  Based on agitation and distance from player.
  2610.     if ((aip->behavior != AIB_RUN_FROM) && (aip->behavior != AIB_STILL) && !(Game_mode & GM_MULTI))
  2611.         if (Overall_agitation > 70) {
  2612.             if ((dist_to_player < F1_0*200) && (rand() < FrameTime/4)) {
  2613.                 if (rand() * (Overall_agitation - 40) > F1_0*5) {
  2614.                     // -- mprintf((0, "(1) Object #%i going from still to path in frame %i.\n", objnum, FrameCount));
  2615.                     create_path_to_player(obj, 4 + Overall_agitation/8 + Difficulty_level, 1);
  2616.                     // -- show_path_and_other(obj);
  2617.                     return;
  2618.                 }
  2619.             }
  2620.         }
  2621.  
  2622.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2623.     //  If retry count not 0, then add it into consecutive_retries.
  2624.     //  If it is 0, cut down consecutive_retries.
  2625.     //  This is largely a hack to speed up physics and deal with stupid AI.  This is low level
  2626.     //  communication between systems of a sort that should not be done.
  2627.     if ((ailp->retry_count) && !(Game_mode & GM_MULTI)) {
  2628.         ailp->consecutive_retries += ailp->retry_count;
  2629.         ailp->retry_count = 0;
  2630.         if (ailp->consecutive_retries > 3) {
  2631.             switch (ailp->mode) {
  2632.                 case AIM_CHASE_OBJECT:
  2633.                     // -- mprintf((0, "(2) Object #%i, retries while chasing, creating path to player in frame %i\n", objnum, FrameCount));
  2634.                     create_path_to_player(obj, 4 + Overall_agitation/8 + Difficulty_level, 1);
  2635.                     break;
  2636.                 case AIM_STILL:
  2637.                     if (!((aip->behavior == AIB_STILL) || (aip->behavior == AIB_STATION)))  //  Behavior is still, so don't follow path.
  2638.                         attempt_to_resume_path(obj);
  2639.                     break;  
  2640.                 case AIM_FOLLOW_PATH:
  2641.                         // mprintf((0, "Object %i following path got %i retries in frame %i\n", obj-Objects, ailp->consecutive_retries, FrameCount));
  2642.                     if (Game_mode & GM_MULTI)
  2643.                         ailp->mode = AIM_STILL;
  2644.                     else
  2645.                         attempt_to_resume_path(obj);
  2646.                     break;
  2647.                 case AIM_RUN_FROM_OBJECT:
  2648.                     move_towards_segment_center(obj);
  2649.                     obj->mtype.phys_info.velocity.x = 0;
  2650.                     obj->mtype.phys_info.velocity.y = 0;
  2651.                     obj->mtype.phys_info.velocity.z = 0;
  2652.                     create_n_segment_path(obj, 5, -1);
  2653.                     ailp->mode = AIM_RUN_FROM_OBJECT;
  2654.                     break;
  2655.                 case AIM_HIDE:
  2656.                     move_towards_segment_center(obj);
  2657.                     obj->mtype.phys_info.velocity.x = 0;
  2658.                     obj->mtype.phys_info.velocity.y = 0;
  2659.                     obj->mtype.phys_info.velocity.z = 0;
  2660.                     // -- mprintf((0, "Hiding, yet creating path to player.\n"));
  2661.                     if (Overall_agitation > (50 - Difficulty_level*4))
  2662.                         create_path_to_player(obj, 4 + Overall_agitation/8, 1);
  2663.                     else {
  2664.                         create_n_segment_path(obj, 5, -1);
  2665.                     }
  2666.                     break;
  2667.                 case AIM_OPEN_DOOR:
  2668.                     create_n_segment_path_to_door(obj, 5, -1);
  2669.                     break;
  2670.                 #ifndef NDEBUG
  2671.                 case AIM_FOLLOW_PATH_2:
  2672.                     Int3(); //  Should never happen!
  2673.                     break;
  2674.                 #endif
  2675.             }
  2676.             ailp->consecutive_retries = 0;
  2677.         }
  2678.     } else
  2679.         ailp->consecutive_retries /= 2;
  2680.  
  2681.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2682.     //  If in materialization center, exit
  2683.     if (!(Game_mode & GM_MULTI) && (Segments[obj->segnum].special == SEGMENT_IS_ROBOTMAKER)) {
  2684.         ai_follow_path(obj, 1);     // 1 = player is visible, which might be a lie, but it works.
  2685.         return;
  2686.     }
  2687.  
  2688.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2689.     //  Decrease player awareness due to the passage of time.
  2690. //-----old, faster way from about 11/06/94----- if (ailp->player_awareness_type) {
  2691. //-----old, faster way from about 11/06/94-----     if (ailp->player_awareness_time > 0) {
  2692. //-----old, faster way from about 11/06/94-----         ailp->player_awareness_time -= FrameTime;
  2693. //-----old, faster way from about 11/06/94-----         if (ailp->player_awareness_time <= 0) {
  2694. //-----old, faster way from about 11/06/94-----                 ailp->player_awareness_time = F1_0*2;
  2695. //-----old, faster way from about 11/06/94-----                 ailp->player_awareness_type--;
  2696. //-----old, faster way from about 11/06/94-----             if (ailp->player_awareness_type < 0) {
  2697. //-----old, faster way from about 11/06/94-----                 aip->GOAL_STATE = AIS_REST;
  2698. //-----old, faster way from about 11/06/94-----                 ailp->player_awareness_type = 0;
  2699. //-----old, faster way from about 11/06/94-----             }
  2700. //-----old, faster way from about 11/06/94-----
  2701. //-----old, faster way from about 11/06/94-----         }
  2702. //-----old, faster way from about 11/06/94-----     } else {
  2703. //-----old, faster way from about 11/06/94-----             ailp->player_awareness_type = 0;
  2704. //-----old, faster way from about 11/06/94-----             aip->GOAL_STATE = AIS_REST;
  2705. //-----old, faster way from about 11/06/94-----     }
  2706. //-----old, faster way from about 11/06/94----- } else
  2707. //-----old, faster way from about 11/06/94-----     aip->GOAL_STATE = AIS_REST;
  2708.  
  2709.  
  2710.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2711.     //  Decrease player awareness due to the passage of time.
  2712.     if (ailp->player_awareness_type) {
  2713.         if (ailp->player_awareness_time > 0) {
  2714.             ailp->player_awareness_time -= FrameTime;
  2715.             if (ailp->player_awareness_time <= 0) {
  2716.                 ailp->player_awareness_time = F1_0*2;   //new: 11/05/94
  2717.                 ailp->player_awareness_type--;  //new: 11/05/94
  2718.             }
  2719.         } else {
  2720.             ailp->player_awareness_type--;
  2721.             ailp->player_awareness_time = F1_0*2;
  2722.             // aip->GOAL_STATE = AIS_REST;
  2723.         }
  2724.     } else
  2725.         aip->GOAL_STATE = AIS_REST;                         //new: 12/13/94
  2726.  
  2727.  
  2728.     if (Player_is_dead && (ailp->player_awareness_type == 0))
  2729.         if ((dist_to_player < F1_0*200) && (rand() < FrameTime/8)) {
  2730.             if ((aip->behavior != AIB_STILL) && (aip->behavior != AIB_RUN_FROM)) {
  2731.                 if (!ai_multiplayer_awareness(obj, 30))
  2732.                     return;
  2733.                 ai_multi_send_robot_position(objnum, -1);
  2734.  
  2735.                 if (!((ailp->mode == AIM_FOLLOW_PATH) && (aip->cur_path_index < aip->path_length-1)))
  2736.                     if (dist_to_player < F1_0*30)
  2737.                         create_n_segment_path(obj, 5, 1);
  2738.                     else
  2739.                         create_path_to_player(obj, 20, 1);
  2740.             }
  2741.         }
  2742.  
  2743.     //  Make sure that if this guy got hit or bumped, then he's chasing player.
  2744.     if ((ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION) || (ailp->player_awareness_type >= PA_PLAYER_COLLISION)) {
  2745.         if ((aip->behavior != AIB_STILL) && (aip->behavior != AIB_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM) && (obj->id != ROBOT_BRAIN))
  2746.             ailp->mode = AIM_CHASE_OBJECT;
  2747.     }
  2748.  
  2749.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2750.     if ((aip->GOAL_STATE == AIS_FLIN) && (aip->CURRENT_STATE == AIS_FLIN))
  2751.         aip->GOAL_STATE = AIS_LOCK;
  2752.  
  2753.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2754.     //  Note: Should only do these two function calls for objects which animate
  2755.     if ((dist_to_player < F1_0*100)) { // && !(Game_mode & GM_MULTI)) {
  2756.         object_animates = do_silly_animation(obj);
  2757.         if (object_animates)
  2758.             ai_frame_animation(obj);
  2759.         //mprintf((0, "Object %i: goal=%i, current=%i\n", obj-Objects, obj->ctype.ai_info.GOAL_STATE, obj->ctype.ai_info.CURRENT_STATE));
  2760.     } else {
  2761.         //  If Object is supposed to animate, but we don't let it animate due to distance, then
  2762.         //  we must change its state, else it will never update.
  2763.         aip->CURRENT_STATE = aip->GOAL_STATE;
  2764.         object_animates = 0;        //  If we're not doing the animation, then should pretend it doesn't animate.
  2765.     }
  2766.  
  2767. //Processed_this_frame++;
  2768. //if (FrameCount != LastFrameCount) {
  2769. //  LastFrameCount = FrameCount;
  2770. //  mprintf((0, "Processed in frame %i = %i robots\n", FrameCount-1, Processed_this_frame));
  2771. //  Processed_this_frame = 0;
  2772. //}
  2773.  
  2774.     switch (Robot_info[obj->id].boss_flag) {
  2775.         case 0:
  2776.             break;
  2777.         case 1:
  2778.             if (aip->GOAL_STATE == AIS_FLIN)
  2779.                 aip->GOAL_STATE = AIS_FIRE;
  2780.             if (aip->CURRENT_STATE == AIS_FLIN)
  2781.                 aip->CURRENT_STATE = AIS_FIRE;
  2782.             dist_to_player /= 4;
  2783.  
  2784.             do_boss_stuff(obj);
  2785.             dist_to_player *= 4;
  2786.             break;
  2787. #ifndef SHAREWARE
  2788.         case 2:
  2789.             if (aip->GOAL_STATE == AIS_FLIN)
  2790.                 aip->GOAL_STATE = AIS_FIRE;
  2791.             if (aip->CURRENT_STATE == AIS_FLIN)
  2792.                 aip->CURRENT_STATE = AIS_FIRE;
  2793.             compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2794.  
  2795.             {   int pv = player_visibility;
  2796.                 fix dtp = dist_to_player/4;
  2797.  
  2798.             //  If player cloaked, visibility is screwed up and superboss will gate in robots when not supposed to.
  2799.             if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
  2800.                 pv = 0;
  2801.                 dtp = vm_vec_dist_quick(&ConsoleObject->pos, &obj->pos)/4;
  2802.             }
  2803.  
  2804.             do_super_boss_stuff(obj, dtp, pv);
  2805.             }
  2806.             break;
  2807. #endif
  2808.         default:
  2809.             Int3(); //  Bogus boss flag value.
  2810.     }
  2811.  
  2812.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2813.     //  Time-slice, don't process all the time, purely an efficiency hack.
  2814.     //  Guys whose behavior is station and are not at their hide segment get processed anyway.
  2815.     if (ailp->player_awareness_type < PA_WEAPON_ROBOT_COLLISION-1) { // If robot got hit, he gets to attack player always!
  2816.         #ifndef NDEBUG
  2817.         if (Break_on_object != objnum) {    //  don't time slice if we're interested in this object.
  2818.         #endif
  2819.             if ((dist_to_player > F1_0*250) && (ailp->time_since_processed <= F1_0*2))
  2820.                 return;
  2821.             else if (!((aip->behavior == AIB_STATION) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->hide_segment != obj->segnum))) {
  2822.                 if ((dist_to_player > F1_0*150) && (ailp->time_since_processed <= F1_0))
  2823.                     return;
  2824.                 else if ((dist_to_player > F1_0*100) && (ailp->time_since_processed <= F1_0/2))
  2825.                     return;
  2826.             }
  2827.         #ifndef NDEBUG
  2828.         }
  2829.         #endif
  2830.     }
  2831.  
  2832. // --   if ((aip->behavior == AIB_STATION) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->hide_segment != obj->segnum))
  2833. // --       mprintf((0, "[%i] ", obj-Objects));
  2834.  
  2835.     //  Reset time since processed, but skew objects so not everything processed synchronously, else
  2836.     //  we get fast frames with the occasional very slow frame.
  2837.     // AI_proc_time = ailp->time_since_processed;
  2838.     ailp->time_since_processed = - ((objnum & 0x03) * FrameTime ) / 2;
  2839.  
  2840.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2841.     //  Perform special ability
  2842.     switch (obj->id) {
  2843.         case ROBOT_BRAIN:
  2844.             //  Robots function nicely if behavior is Station.  This means they won't move until they
  2845.             //  can see the player, at which time they will start wandering about opening doors.
  2846.             if (ConsoleObject->segnum == obj->segnum) {
  2847.                 if (!ai_multiplayer_awareness(obj, 97))
  2848.                     return;
  2849.                 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2850.                 move_away_from_player(obj, &vec_to_player, 0);
  2851.                 ai_multi_send_robot_position(objnum, -1);
  2852.             } else if (ailp->mode != AIM_STILL) {
  2853.                 int r;
  2854.  
  2855.                 r = openable_doors_in_segment(obj);
  2856.                 if (r != -1) {
  2857.                     ailp->mode = AIM_OPEN_DOOR;
  2858.                     aip->GOALSIDE = r;
  2859.                 } else if (ailp->mode != AIM_FOLLOW_PATH) {
  2860.                     if (!ai_multiplayer_awareness(obj, 50))
  2861.                         return;
  2862.                     create_n_segment_path_to_door(obj, 8+Difficulty_level, -1);     //  third parameter is avoid_seg, -1 means avoid nothing.
  2863.                     ai_multi_send_robot_position(objnum, -1);
  2864.                 }
  2865.             } else {
  2866.                 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2867.                 if (player_visibility) {
  2868.                     if (!ai_multiplayer_awareness(obj, 50))
  2869.                         return;
  2870.                     create_n_segment_path_to_door(obj, 8+Difficulty_level, -1);     //  third parameter is avoid_seg, -1 means avoid nothing.
  2871.                     ai_multi_send_robot_position(objnum, -1);
  2872.                 }
  2873.             }
  2874.             break;
  2875.         default:
  2876.             break;
  2877.     }
  2878.  
  2879.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  2880.     switch (ailp->mode) {
  2881.         case AIM_CHASE_OBJECT: {        // chasing player, sort of, chase if far, back off if close, circle in between
  2882.             fix circle_distance;
  2883.  
  2884.             circle_distance = robptr->circle_distance[Difficulty_level] + ConsoleObject->size;
  2885.             //  Green guy doesn't get his circle distance boosted, else he might never attack.
  2886.             if (robptr->attack_type != 1)
  2887.                 circle_distance += (objnum&0xf) * F1_0/2;
  2888.  
  2889.             compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2890.  
  2891.             //  @mk, 12/27/94, structure here was strange.  Would do both clauses of what are now this if/then/else.  Used to be if/then, if/then.
  2892.             if ((player_visibility < 2) && (previous_visibility == 2)) { // this is redundant: mk, 01/15/95: && (ailp->mode == AIM_CHASE_OBJECT)) {
  2893.                 // mprintf((0, "I used to be able to see the player!\n"));
  2894.                 if (!ai_multiplayer_awareness(obj, 53)) {
  2895.                     if (maybe_ai_do_actual_firing_stuff(obj, aip))
  2896.                         ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  2897.                     return;
  2898.                 }
  2899.                 // -- mprintf((0, "(3) Object #%i going from chase to player path in frame %i.\n", objnum, FrameCount));
  2900.                 create_path_to_player(obj, 8, 1);
  2901.                 // -- show_path_and_other(obj);
  2902.                 ai_multi_send_robot_position(objnum, -1);
  2903.             } else if ((player_visibility == 0) && (dist_to_player > F1_0*80) && (!(Game_mode & GM_MULTI))) {
  2904.                 //  If pretty far from the player, player cannot be seen (obstructed) and in chase mode, switch to follow path mode.
  2905.                 //  This has one desirable benefit of avoiding physics retries.
  2906.                 if (aip->behavior == AIB_STATION) {
  2907.                     ailp->goal_segment = aip->hide_segment;
  2908.                     // -- mprintf((0, "(1) Object #%i going from chase to STATION in frame %i.\n", objnum, FrameCount));
  2909.                     create_path_to_station(obj, 15);
  2910.                     // -- show_path_and_other(obj);
  2911.                 } else
  2912.                     create_n_segment_path(obj, 5, -1);
  2913.                 break;
  2914.             }
  2915.  
  2916.             if ((aip->CURRENT_STATE == AIS_REST) && (aip->GOAL_STATE == AIS_REST)) {
  2917.                 if (player_visibility) {
  2918.                     if (rand() < FrameTime*player_visibility) {
  2919.                         if (dist_to_player/256 < rand()*player_visibility) {
  2920.                             // mprintf((0, "Object %i searching for player.\n", obj-Objects));
  2921.                             aip->GOAL_STATE = AIS_SRCH;
  2922.                             aip->CURRENT_STATE = AIS_SRCH;
  2923.                         }
  2924.                     }
  2925.                 }
  2926.             }
  2927.  
  2928.             if (GameTime - ailp->time_player_seen > CHASE_TIME_LENGTH) {
  2929.  
  2930.                 if (Game_mode & GM_MULTI)
  2931.                     if (!player_visibility && (dist_to_player > F1_0*70)) {
  2932.                         ailp->mode = AIM_STILL;
  2933.                         return;
  2934.                     }
  2935.  
  2936.                 if (!ai_multiplayer_awareness(obj, 64)) {
  2937.                     if (maybe_ai_do_actual_firing_stuff(obj, aip))
  2938.                         ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  2939.                     return;
  2940.                 }
  2941.                 // -- mprintf((0, "(4) Object #%i going from chase to player path in frame %i.\n", objnum, FrameCount));
  2942.                 create_path_to_player(obj, 10, 1);
  2943.                 // -- show_path_and_other(obj);
  2944.                 ai_multi_send_robot_position(objnum, -1);
  2945.             } else if ((aip->CURRENT_STATE != AIS_REST) && (aip->GOAL_STATE != AIS_REST)) {
  2946.                 if (!ai_multiplayer_awareness(obj, 70)) {
  2947.                     if (maybe_ai_do_actual_firing_stuff(obj, aip))
  2948.                         ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  2949.                     return;
  2950.                 }
  2951.                 ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, circle_distance, 0);
  2952.  
  2953.                 if ((obj_ref & 1) && ((aip->GOAL_STATE == AIS_SRCH) || (aip->GOAL_STATE == AIS_LOCK))) {
  2954.                     if (player_visibility) // == 2)
  2955.                         ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  2956.                     else
  2957.                         ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  2958.                 }
  2959.  
  2960.                 if (ai_evaded) {
  2961.                     ai_multi_send_robot_position(objnum, 1);
  2962.                     ai_evaded = 0;
  2963.                 }
  2964.                 else 
  2965.                     ai_multi_send_robot_position(objnum, -1);
  2966.                 
  2967.                 do_firing_stuff(obj, player_visibility, &vec_to_player);
  2968.             }
  2969.             break;
  2970.         }
  2971.  
  2972.         case AIM_RUN_FROM_OBJECT:
  2973.             compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  2974.  
  2975.             if (player_visibility) {
  2976.                 if (ailp->player_awareness_type == 0)
  2977.                     ailp->player_awareness_type = PA_WEAPON_ROBOT_COLLISION;
  2978.  
  2979.             }
  2980.  
  2981.             //  If in multiplayer, only do if player visible.  If not multiplayer, do always.
  2982.             if (!(Game_mode & GM_MULTI) || player_visibility)
  2983.                 if (ai_multiplayer_awareness(obj, 75)) {
  2984.                     ai_follow_path(obj, player_visibility);
  2985.                     ai_multi_send_robot_position(objnum, -1);
  2986.                 }
  2987.  
  2988.             if (aip->GOAL_STATE != AIS_FLIN)
  2989.                 aip->GOAL_STATE = AIS_LOCK;
  2990.             else if (aip->CURRENT_STATE == AIS_FLIN)
  2991.                 aip->GOAL_STATE = AIS_LOCK;
  2992.  
  2993.             //  Bad to let run_from robot fire at player because it will cause a war in which it turns towards the
  2994.             //  player to fire and then towards its goal to move.
  2995.             // do_firing_stuff(obj, player_visibility, &vec_to_player);
  2996.             //  Instead, do this:
  2997.             //  (Note, only drop if player is visible.  This prevents the bombs from being a giveaway, and
  2998.             //  also ensures that the robot is moving while it is dropping.  Also means fewer will be dropped.)
  2999.             if ((ailp->next_fire <= 0) && (player_visibility)) {
  3000.                 vms_vector  fire_vec, fire_pos;
  3001.  
  3002.                 if (!ai_multiplayer_awareness(obj, 75))
  3003.                     return;
  3004.  
  3005.                 fire_vec = obj->orient.fvec;
  3006.                 vm_vec_negate(&fire_vec);
  3007.                 vm_vec_add(&fire_pos, &obj->pos, &fire_vec);
  3008.  
  3009.                 Laser_create_new_easy( &fire_vec, &fire_pos, obj-Objects, PROXIMITY_ID, 1);
  3010.                 ailp->next_fire = F1_0*5;       //  Drop a proximity bomb every 5 seconds.
  3011.                 
  3012.                 #ifdef NETWORK
  3013.                 if (Game_mode & GM_MULTI)
  3014.                 {
  3015.                     ai_multi_send_robot_position(obj-Objects, -1);
  3016.                     multi_send_robot_fire(obj-Objects, -1, &fire_vec);
  3017.                 }                 
  3018.                 #endif  
  3019.             }
  3020.             break;
  3021.  
  3022.         case AIM_FOLLOW_PATH: {
  3023.             int anger_level = 65;
  3024.  
  3025.             if (aip->behavior == AIB_STATION)
  3026.                 if (Point_segs[aip->hide_index + aip->path_length - 1].segnum == aip->hide_segment) {
  3027.                     anger_level = 64;
  3028.                     // mprintf((0, "Object %i, station, lowering anger to 64.\n"));
  3029.                 }
  3030.  
  3031.             compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3032.  
  3033.             if (Game_mode & (GM_MODEM | GM_SERIAL))
  3034.                 if (!player_visibility && (dist_to_player > F1_0*70)) {
  3035.                     ailp->mode = AIM_STILL;
  3036.                     return;
  3037.                 }
  3038.  
  3039.             if (!ai_multiplayer_awareness(obj, anger_level)) {
  3040.                 if (maybe_ai_do_actual_firing_stuff(obj, aip)) {
  3041.                     compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3042.                     ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3043.                 }
  3044.                 return;
  3045.             }
  3046.  
  3047.             ai_follow_path(obj, player_visibility);
  3048.  
  3049.             if (aip->GOAL_STATE != AIS_FLIN)
  3050.                 aip->GOAL_STATE = AIS_LOCK;
  3051.             else if (aip->CURRENT_STATE == AIS_FLIN)
  3052.                 aip->GOAL_STATE = AIS_LOCK;
  3053.  
  3054.             if ((aip->behavior != AIB_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM))
  3055.                 do_firing_stuff(obj, player_visibility, &vec_to_player);
  3056.  
  3057.             if ((player_visibility == 2) && (aip->behavior != AIB_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM) && (obj->id != ROBOT_BRAIN)) {
  3058.                 if (robptr->attack_type == 0)
  3059.                     ailp->mode = AIM_CHASE_OBJECT;
  3060.             } else if ((player_visibility == 0) && (aip->behavior == AIB_NORMAL) && (ailp->mode == AIM_FOLLOW_PATH) && (aip->behavior != AIB_RUN_FROM)) {
  3061.                 ailp->mode = AIM_STILL;
  3062.                 aip->hide_index = -1;
  3063.                 aip->path_length = 0;
  3064.             }
  3065.  
  3066.             ai_multi_send_robot_position(objnum, -1);
  3067.  
  3068.             break;
  3069.         }
  3070.  
  3071.         case AIM_HIDE:
  3072.             if (!ai_multiplayer_awareness(obj, 71)) {
  3073.                 if (maybe_ai_do_actual_firing_stuff(obj, aip)) {
  3074.                     compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3075.                     ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3076.                 }
  3077.                 return;
  3078.             }
  3079.  
  3080.             compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3081.  
  3082.             ai_follow_path(obj, player_visibility);
  3083.  
  3084.             if (aip->GOAL_STATE != AIS_FLIN)
  3085.                 aip->GOAL_STATE = AIS_LOCK;
  3086.             else if (aip->CURRENT_STATE == AIS_FLIN)
  3087.                 aip->GOAL_STATE = AIS_LOCK;
  3088.  
  3089.             ai_multi_send_robot_position(objnum, -1);
  3090.             break;
  3091.  
  3092.         case AIM_STILL:
  3093.             if ((dist_to_player < F1_0*120+Difficulty_level*F1_0*20) || (ailp->player_awareness_type >= PA_WEAPON_ROBOT_COLLISION-1)) {
  3094.                 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3095.  
  3096.                 //  turn towards vector if visible this time or last time, or rand
  3097.                 // new!
  3098.                 if ((player_visibility) || (previous_visibility) || ((rand() > 0x4000) && !(Game_mode & GM_MULTI))) {
  3099.                     if (!ai_multiplayer_awareness(obj, 71)) {
  3100.                         if (maybe_ai_do_actual_firing_stuff(obj, aip))
  3101.                             ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3102.                         return;
  3103.                     }
  3104.                     ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  3105.                     ai_multi_send_robot_position(objnum, -1);
  3106.                 }
  3107.  
  3108.                 do_firing_stuff(obj, player_visibility, &vec_to_player);
  3109.                 //  This is debugging code!  Remove it!  It's to make the green guy attack without doing other kinds of movement.
  3110.                 if (player_visibility) {        //  Change, MK, 01/03/94 for Multiplayer reasons.  If robots can't see you (even with eyes on back of head), then don't do evasion.
  3111.                     if (robptr->attack_type == 1) {
  3112.                         aip->behavior = AIB_NORMAL;
  3113.                         if (!ai_multiplayer_awareness(obj, 80)) {
  3114.                             if (maybe_ai_do_actual_firing_stuff(obj, aip))
  3115.                                 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3116.                             return;
  3117.                         }
  3118.                         ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, 0, 0);
  3119.                         if (ai_evaded) {
  3120.                             ai_multi_send_robot_position(objnum, 1);
  3121.                             ai_evaded = 0;
  3122.                         }
  3123.                         else
  3124.                             ai_multi_send_robot_position(objnum, -1);
  3125.                     } else {
  3126.                         //  Robots in hover mode are allowed to evade at half normal speed.
  3127.                         if (!ai_multiplayer_awareness(obj, 81)) {
  3128.                             if (maybe_ai_do_actual_firing_stuff(obj, aip))
  3129.                                 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3130.                             return;
  3131.                         }
  3132.                         ai_move_relative_to_player(obj, ailp, dist_to_player, &vec_to_player, 0, 1);
  3133.                         if (ai_evaded) {
  3134.                             ai_multi_send_robot_position(objnum, -1);
  3135.                             ai_evaded = 0;
  3136.                         }
  3137.                         else                
  3138.                             ai_multi_send_robot_position(objnum, -1);
  3139.                     }
  3140.                 } else if ((obj->segnum != aip->hide_segment) && (dist_to_player > F1_0*80) && (!(Game_mode & GM_MULTI))) {
  3141.                     //  If pretty far from the player, player cannot be seen (obstructed) and in chase mode, switch to follow path mode.
  3142.                     //  This has one desirable benefit of avoiding physics retries.
  3143.                     if (aip->behavior == AIB_STATION) {
  3144.                         ailp->goal_segment = aip->hide_segment;
  3145.                         // -- mprintf((0, "(2) Object #%i going from STILL to STATION in frame %i.\n", objnum, FrameCount));
  3146.                         create_path_to_station(obj, 15);
  3147.                         // -- show_path_and_other(obj);
  3148.                     }
  3149.                     break;
  3150.                 }
  3151.             }
  3152.  
  3153.             break;
  3154.         case AIM_OPEN_DOOR: {       // trying to open a door.
  3155.             vms_vector  center_point, goal_vector;
  3156.             Assert(obj->id == ROBOT_BRAIN);     //  Make sure this guy is allowed to be in this mode.
  3157.  
  3158.             if (!ai_multiplayer_awareness(obj, 62))
  3159.                 return;
  3160.             compute_center_point_on_side(¢er_point, &Segments[obj->segnum], aip->GOALSIDE);
  3161.             vm_vec_sub(&goal_vector, ¢er_point, &obj->pos);
  3162.             vm_vec_normalize_quick(&goal_vector);
  3163.             ai_turn_towards_vector(&goal_vector, obj, robptr->turn_time[Difficulty_level]);
  3164.             move_towards_vector(obj, &goal_vector);
  3165.             ai_multi_send_robot_position(objnum, -1);
  3166.  
  3167.             break;
  3168.         }
  3169.  
  3170.         default:
  3171.             mprintf((0, "Unknown mode = %i in robot %i, behavior = %i\n", ailp->mode, obj-Objects, aip->behavior));
  3172.             ailp->mode = AIM_CHASE_OBJECT;
  3173.             break;
  3174.     }       // end: switch (ailp->mode) {
  3175.  
  3176.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  3177.     //  If the robot can see you, increase his awareness of you.
  3178.     //  This prevents the problem of a robot looking right at you but doing nothing.
  3179.     // Assert(player_visibility != -1); //  Means it didn't get initialized!
  3180.     compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3181.     if (player_visibility == 2)
  3182.         if (ailp->player_awareness_type == 0)
  3183.             ailp->player_awareness_type = PA_PLAYER_COLLISION;
  3184.  
  3185.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  3186.     if (!object_animates) {
  3187.         aip->CURRENT_STATE = aip->GOAL_STATE;
  3188.         // mprintf((0, "Setting current to goal (%i) because object doesn't animate.\n", aip->GOAL_STATE));
  3189.     }
  3190.  
  3191.     Assert(ailp->player_awareness_type <= AIE_MAX);
  3192.     Assert(aip->CURRENT_STATE < AIS_MAX);
  3193.     Assert(aip->GOAL_STATE < AIS_MAX);
  3194.  
  3195.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  3196.     if (ailp->player_awareness_type) {
  3197.         new_goal_state = Ai_transition_table[ailp->player_awareness_type-1][aip->CURRENT_STATE][aip->GOAL_STATE];
  3198.         if (ailp->player_awareness_type == PA_WEAPON_ROBOT_COLLISION) {
  3199.             //  Decrease awareness, else this robot will flinch every frame.
  3200.             ailp->player_awareness_type--;
  3201.             ailp->player_awareness_time = F1_0*3;
  3202.         }
  3203.  
  3204.         if (new_goal_state == AIS_ERR_)
  3205.             new_goal_state = AIS_REST;
  3206.  
  3207.         if (aip->CURRENT_STATE == AIS_NONE)
  3208.             aip->CURRENT_STATE = AIS_REST;
  3209.  
  3210.         aip->GOAL_STATE = new_goal_state;
  3211.  
  3212.     }
  3213.  
  3214.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  3215.     //  If new state = fire, then set all gun states to fire.
  3216.     if ((aip->GOAL_STATE == AIS_FIRE) ) {
  3217.         int i,num_guns;
  3218.         num_guns = Robot_info[obj->id].n_guns;
  3219.         for (i=0; i<num_guns; i++)
  3220.             ailp->goal_state[i] = AIS_FIRE;
  3221.     }
  3222.  
  3223.     //  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -  - -
  3224.     //  Hack by mk on 01/04/94, if a guy hasn't animated to the firing state, but his next_fire says ok to fire, bash him there
  3225.     if ((ailp->next_fire < 0) && (aip->GOAL_STATE == AIS_FIRE))
  3226.         aip->CURRENT_STATE = AIS_FIRE;
  3227.  
  3228.     if ((aip->GOAL_STATE != AIS_FLIN)  && (obj->id != ROBOT_BRAIN)) {
  3229.         switch (aip->CURRENT_STATE) {
  3230.             case    AIS_NONE:
  3231.                 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3232.  
  3233.                 dot = vm_vec_dot(&obj->orient.fvec, &vec_to_player);
  3234.                 if (dot >= F1_0/2)
  3235.                     if (aip->GOAL_STATE == AIS_REST)
  3236.                         aip->GOAL_STATE = AIS_SRCH;
  3237.                 //mprintf((0, "State = none, goal = %i.\n", aip->GOAL_STATE));
  3238.                 break;
  3239.             case    AIS_REST:
  3240.                 if (aip->GOAL_STATE == AIS_REST) {
  3241.                     compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3242.                     if ((ailp->next_fire <= 0) && (player_visibility)) {
  3243.                         // mprintf((0, "Setting goal state to fire from rest.\n"));
  3244.                         aip->GOAL_STATE = AIS_FIRE;
  3245.                     }
  3246.                     //mprintf((0, "State = rest, goal = %i.\n", aip->GOAL_STATE));
  3247.                 }
  3248.                 break;
  3249.             case    AIS_SRCH:
  3250.                 if (!ai_multiplayer_awareness(obj, 60))
  3251.                     return;
  3252.  
  3253.                 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3254.  
  3255.                 if (player_visibility) {
  3256.                     ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  3257.                     ai_multi_send_robot_position(objnum, -1);
  3258.                 } else if (!(Game_mode & GM_MULTI))
  3259.                     ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  3260.                 break;
  3261.             case    AIS_LOCK:
  3262.                 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3263.  
  3264.                 if (!(Game_mode & GM_MULTI) || (player_visibility)) {
  3265.                     if (!ai_multiplayer_awareness(obj, 68))
  3266.                         return;
  3267.  
  3268.                     if (player_visibility) {
  3269.                         ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  3270.                         ai_multi_send_robot_position(objnum, -1);
  3271.                     } else if (!(Game_mode & GM_MULTI))
  3272.                         ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  3273.                 }
  3274.                 break;
  3275.             case    AIS_FIRE:
  3276.                 compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3277.  
  3278.                 if (player_visibility) {
  3279.                     if (!ai_multiplayer_awareness(obj, (ROBOT_FIRE_AGITATION-1))) 
  3280.                     {
  3281.                         if (Game_mode & GM_MULTI) {
  3282.                             ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3283.                             return;
  3284.                         }
  3285.                     }
  3286.                     ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  3287.                     ai_multi_send_robot_position(objnum, -1);
  3288.                 } else if (!(Game_mode & GM_MULTI)) {
  3289.                     ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  3290.                 }
  3291.  
  3292.                 //  Fire at player, if appropriate.
  3293.                 ai_do_actual_firing_stuff(obj, aip, ailp, robptr, &vec_to_player, dist_to_player, &gun_point, player_visibility, object_animates);
  3294.  
  3295.                 break;
  3296.             case    AIS_RECO:
  3297.                 if (!(obj_ref & 3)) {
  3298.                     compute_vis_and_vec(obj, &vis_vec_pos, ailp, &vec_to_player, &player_visibility, robptr, &visibility_and_vec_computed);
  3299.                     if (player_visibility) {
  3300.                         if (!ai_multiplayer_awareness(obj, 69))
  3301.                             return;
  3302.                         ai_turn_towards_vector(&vec_to_player, obj, robptr->turn_time[Difficulty_level]);
  3303.                         ai_multi_send_robot_position(objnum, -1);
  3304.                     } else if (!(Game_mode & GM_MULTI)) {
  3305.                         ai_turn_randomly(&vec_to_player, obj, robptr->turn_time[Difficulty_level], previous_visibility);
  3306.                     }
  3307.                 }
  3308.                 break;
  3309.             case    AIS_FLIN:
  3310.                 // mprintf((0, "State = flinch, goal = %i.\n", aip->GOAL_STATE));
  3311.                 break;
  3312.             default:
  3313.                 mprintf((1, "Unknown mode for AI object #%i\n", objnum));
  3314.                 aip->GOAL_STATE = AIS_REST;
  3315.                 aip->CURRENT_STATE = AIS_REST;
  3316.                 break;
  3317.         }
  3318.     } // end of: if (aip->GOAL_STATE != AIS_FLIN) {
  3319.  
  3320.     // Switch to next gun for next fire.
  3321.     if (player_visibility == 0) {
  3322.         aip->CURRENT_GUN++;
  3323.         if (aip->CURRENT_GUN >= Robot_info[obj->id].n_guns)
  3324.             aip->CURRENT_GUN = 0;
  3325.     }
  3326.  
  3327. }
  3328.  
  3329. //--mk, 121094 -- // ----------------------------------------------------------------------------------
  3330. //--mk, 121094 -- void spin_robot(object *robot, vms_vector *collision_point)
  3331. //--mk, 121094 -- {
  3332. //--mk, 121094 --   if (collision_point->x != 3) {
  3333. //--mk, 121094 --       robot->phys_info.rotvel.x = 0x1235;
  3334. //--mk, 121094 --       robot->phys_info.rotvel.y = 0x2336;
  3335. //--mk, 121094 --       robot->phys_info.rotvel.z = 0x3737;
  3336. //--mk, 121094 --   }
  3337. //--mk, 121094 -- 
  3338. //--mk, 121094 -- }
  3339.  
  3340. //  -----------------------------------------------------------------------------------
  3341. void ai_do_cloak_stuff(void)
  3342. {
  3343.     int i;
  3344.  
  3345.     for (i=0; i<MAX_AI_CLOAK_INFO; i++) {
  3346.         Ai_cloak_info[i].last_position = ConsoleObject->pos;
  3347.         Ai_cloak_info[i].last_time = GameTime;
  3348.     }
  3349.  
  3350.     //  Make work for control centers.
  3351.     Believed_player_pos = Ai_cloak_info[0].last_position;
  3352.  
  3353. }
  3354.  
  3355. //  -----------------------------------------------------------------------------------
  3356. //  Returns false if awareness is considered too puny to add, else returns true.
  3357. int add_awareness_event(object *objp, int type)
  3358. {
  3359.     //  If player cloaked and hit a robot, then increase awareness
  3360.     if ((type == PA_WEAPON_ROBOT_COLLISION) || (type == PA_WEAPON_WALL_COLLISION) || (type == PA_PLAYER_COLLISION))
  3361.         ai_do_cloak_stuff();
  3362.  
  3363.     if (Num_awareness_events < MAX_AWARENESS_EVENTS) {
  3364.         if ((type == PA_WEAPON_WALL_COLLISION) || (type == PA_WEAPON_ROBOT_COLLISION))
  3365.             if (objp->id == VULCAN_ID)
  3366.                 if (rand() > 3276)
  3367.                     return 0;       //  For vulcan cannon, only about 1/10 actually cause awareness
  3368.  
  3369.         Awareness_events[Num_awareness_events].segnum = objp->segnum;
  3370.         Awareness_events[Num_awareness_events].pos = objp->pos;
  3371.         Awareness_events[Num_awareness_events].type = type;
  3372.         Num_awareness_events++;
  3373.     } else
  3374.         Assert(0);      // Hey -- Overflowed Awareness_events, make more or something
  3375.                             // This just gets ignored, so you can just continue.
  3376.     return 1;
  3377.  
  3378. }
  3379.  
  3380. // ----------------------------------------------------------------------------------
  3381. //  Robots will become aware of the player based on something that occurred.
  3382. //  The object (probably player or weapon) which created the awareness is objp.
  3383. void create_awareness_event(object *objp, int type)
  3384. {
  3385.     if (add_awareness_event(objp, type)) {
  3386.         if (((rand() * (type+4)) >> 15) > 4)
  3387.             Overall_agitation++;
  3388.         if (Overall_agitation > OVERALL_AGITATION_MAX)
  3389.             Overall_agitation = OVERALL_AGITATION_MAX;
  3390.     }
  3391. }
  3392.  
  3393. byte    New_awareness[MAX_SEGMENTS];
  3394.  
  3395. // ----------------------------------------------------------------------------------
  3396. void pae_aux(int segnum, int type, int level)
  3397. {
  3398.     int j;
  3399.  
  3400.     if (New_awareness[segnum] < type)
  3401.         New_awareness[segnum] = type;
  3402.  
  3403.     // Process children.
  3404.     if (level <= 4)
  3405.         for (j=0; j<MAX_SIDES_PER_SEGMENT; j++)
  3406.             if (IS_CHILD(Segments[segnum].children[j]))
  3407.                 if (type == 4)
  3408.                     pae_aux(Segments[segnum].children[j], type-1, level+1);
  3409.                 else
  3410.                     pae_aux(Segments[segnum].children[j], type, level+1);
  3411. }
  3412.  
  3413.  
  3414. // ----------------------------------------------------------------------------------
  3415. void process_awareness_events(void)
  3416. {
  3417.     int i;
  3418.  
  3419.     for (i=0; i<=Highest_segment_index; i++)
  3420.         New_awareness[i] = 0;
  3421.  
  3422.     for (i=0; i<Num_awareness_events; i++)
  3423.         pae_aux(Awareness_events[i].segnum, Awareness_events[i].type, 1);
  3424.  
  3425.     Num_awareness_events = 0;
  3426. }
  3427.  
  3428. // ----------------------------------------------------------------------------------
  3429. void set_player_awareness_all(void)
  3430. {
  3431.     int i;
  3432.  
  3433.     process_awareness_events();
  3434.  
  3435.     for (i=0; i<=Highest_object_index; i++)
  3436.         if (Objects[i].control_type == CT_AI)
  3437.             if (New_awareness[Objects[i].segnum] > Ai_local_info[i].player_awareness_type) {
  3438.                 Ai_local_info[i].player_awareness_type = New_awareness[Objects[i].segnum];
  3439.                 Ai_local_info[i].player_awareness_time = PLAYER_AWARENESS_INITIAL_TIME;
  3440.             }
  3441. }
  3442.  
  3443. #ifndef NDEBUG
  3444. int Ai_dump_enable = 0;
  3445.  
  3446. FILE *Ai_dump_file = NULL;
  3447.  
  3448. char    Ai_error_message[128] = "";
  3449.  
  3450. // ----------------------------------------------------------------------------------
  3451. void dump_ai_objects_all()
  3452. {
  3453. #if PARALLAX
  3454.     int objnum;
  3455.     int total=0;
  3456.     time_t  time_of_day;
  3457.  
  3458.     time_of_day = time(NULL);
  3459.  
  3460.     if (!Ai_dump_enable)
  3461.         return;
  3462.  
  3463.     if (Ai_dump_file == NULL)
  3464.         Ai_dump_file = fopen("ai.out","a+t");
  3465.  
  3466.     fprintf(Ai_dump_file, "\nnum: seg distance __mode__ behav.    [velx vely velz] (Frame = %i)\n", FrameCount);
  3467.     fprintf(Ai_dump_file, "Date & Time = %s\n", ctime(&time_of_day));
  3468.  
  3469.     if (Ai_error_message[0])
  3470.         fprintf(Ai_dump_file, "Error message: %s\n", Ai_error_message);
  3471.  
  3472.     for (objnum=0; objnum <= Highest_object_index; objnum++) {
  3473.         object      *objp = &Objects[objnum];
  3474.         ai_static   *aip = &objp->ctype.ai_info;
  3475.         ai_local        *ailp = &Ai_local_info[objnum];
  3476.         fix         dist_to_player;
  3477.  
  3478.         dist_to_player = vm_vec_dist(&objp->pos, &ConsoleObject->pos);
  3479.  
  3480.         if (objp->control_type == CT_AI) {
  3481.             fprintf(Ai_dump_file, "%3i: %3i %8.3f %8s %8s [%3i %4i]\n",
  3482.                 objnum, objp->segnum, f2fl(dist_to_player), mode_text[ailp->mode], behavior_text[aip->behavior-0x80], aip->hide_index, aip->path_length);
  3483.             if (aip->path_length)
  3484.                 total += aip->path_length;
  3485.         }
  3486.     }
  3487.  
  3488.     fprintf(Ai_dump_file, "Total path length = %4i\n", total);
  3489. #endif
  3490.  
  3491. }
  3492.  
  3493. // ----------------------------------------------------------------------------------
  3494. void force_dump_ai_objects_all(char *msg)
  3495. {
  3496.     int tsave;
  3497.  
  3498.     tsave = Ai_dump_enable;
  3499.  
  3500.     Ai_dump_enable = 1;
  3501.  
  3502.     sprintf(Ai_error_message, "%s\n", msg);
  3503.     dump_ai_objects_all();
  3504.     Ai_error_message[0] = 0;
  3505.  
  3506.     Ai_dump_enable = tsave;
  3507. }
  3508.  
  3509. // ----------------------------------------------------------------------------------
  3510. void turn_off_ai_dump(void)
  3511. {
  3512.     if (Ai_dump_file != NULL)
  3513.         fclose(Ai_dump_file);
  3514.  
  3515.     Ai_dump_file = NULL;
  3516. }
  3517.  
  3518. #endif
  3519.  
  3520. // ----------------------------------------------------------------------------------
  3521. //  Do things which need to get done for all AI objects each frame.
  3522. //  This includes:
  3523. //      Setting player_awareness (a fix, time in seconds which object is aware of player)
  3524. void do_ai_frame_all(void)
  3525. {
  3526. #ifndef NDEBUG
  3527.     dump_ai_objects_all();
  3528. #endif
  3529.  
  3530.     set_player_awareness_all();
  3531.  
  3532. //  if ((FrameCount & 0x07) == 0)
  3533. //      mprintf((0, "[%i] ", Overall_agitation));
  3534. }
  3535.  
  3536. //--unused-- // ----------------------------------------------------------------------------------
  3537. //--unused-- // Reset various state information for a new life.
  3538. //--unused-- // Probably not going to be called for a new game because for that an entire object
  3539. //--unused-- // gets reloaded.
  3540. //--unused-- void reset_ai_states(object *objp)
  3541. //--unused-- {
  3542. //--unused--    int     i;
  3543. //--unused--    //ai_static *aip = &objp->ctype.ai_info;
  3544. //--unused--    ai_local        *ailp = &Ai_local_info[objp-Objects];
  3545. //--unused-- 
  3546. //--unused--    ailp->wait_time = 0;
  3547. //--unused--    ailp->next_fire = 0;
  3548. //--unused--    ailp->player_awareness_type = 0;
  3549. //--unused--    for (i=0; i<MAX_SUBMODELS; i++) {
  3550. //--unused--        ailp->goal_angles[i].p = 0;     ailp->goal_angles[i].b = 0;     ailp->goal_angles[i].h = 0;
  3551. //--unused--        ailp->delta_angles[i].p = 0;    ailp->delta_angles[i].b = 0;    ailp->delta_angles[i].h = 0;
  3552. //--unused--        ailp->goal_state[i] = 0;
  3553. //--unused--        ailp->achieved_state[i] = 0;
  3554. //--unused--    }
  3555. //--unused-- }
  3556.  
  3557. //  Initializations to be performed for all robots for a new level.
  3558. void init_robots_for_level(void)
  3559. {
  3560.     Overall_agitation = 0;
  3561. }
  3562.  
  3563. int ai_save_state( FILE * fp )
  3564. {
  3565.     fwrite( &Ai_initialized, sizeof(int), 1, fp );
  3566.     fwrite( &Overall_agitation, sizeof(int), 1, fp );
  3567.     fwrite( Ai_local_info, sizeof(ai_local)*MAX_OBJECTS, 1, fp );
  3568.     fwrite( Point_segs, sizeof(point_seg)*MAX_POINT_SEGS, 1, fp );
  3569.     fwrite( Ai_cloak_info, sizeof(ai_cloak_info)*MAX_AI_CLOAK_INFO, 1, fp );
  3570.     fwrite( &Boss_cloak_start_time, sizeof(fix), 1, fp );
  3571.     fwrite( &Boss_cloak_end_time , sizeof(fix), 1, fp );
  3572.     fwrite( &Last_teleport_time , sizeof(fix), 1, fp );
  3573.     fwrite( &Boss_teleport_interval, sizeof(fix), 1, fp );
  3574.     fwrite( &Boss_cloak_interval, sizeof(fix), 1, fp );
  3575.     fwrite( &Boss_cloak_duration, sizeof(fix), 1, fp );
  3576.     fwrite( &Last_gate_time, sizeof(fix), 1, fp );
  3577.     fwrite( &Gate_interval, sizeof(fix), 1, fp );
  3578.     fwrite( &Boss_dying_start_time, sizeof(fix), 1, fp );
  3579.     fwrite( &Boss_dying, sizeof(int), 1, fp );
  3580.     fwrite( &Boss_dying_sound_playing, sizeof(int), 1, fp );
  3581.     fwrite( &Boss_hit_this_frame, sizeof(int), 1, fp );
  3582.     fwrite( &Boss_been_hit, sizeof(int), 1, fp );
  3583.     return 1;
  3584. }
  3585.  
  3586. int ai_restore_state( FILE * fp )
  3587. {
  3588.     fread( &Ai_initialized, sizeof(int), 1, fp );
  3589.     fread( &Overall_agitation, sizeof(int), 1, fp );
  3590.     fread( Ai_local_info, sizeof(ai_local)*MAX_OBJECTS, 1, fp );
  3591.     fread( Point_segs, sizeof(point_seg)*MAX_POINT_SEGS, 1, fp );
  3592.     fread( Ai_cloak_info, sizeof(ai_cloak_info)*MAX_AI_CLOAK_INFO, 1, fp );
  3593.     fread( &Boss_cloak_start_time, sizeof(fix), 1, fp );
  3594.     fread( &Boss_cloak_end_time , sizeof(fix), 1, fp );
  3595.     fread( &Last_teleport_time , sizeof(fix), 1, fp );
  3596.     fread( &Boss_teleport_interval, sizeof(fix), 1, fp );
  3597.     fread( &Boss_cloak_interval, sizeof(fix), 1, fp );
  3598.     fread( &Boss_cloak_duration, sizeof(fix), 1, fp );
  3599.     fread( &Last_gate_time, sizeof(fix), 1, fp );
  3600.     fread( &Gate_interval, sizeof(fix), 1, fp );
  3601.     fread( &Boss_dying_start_time, sizeof(fix), 1, fp );
  3602.     fread( &Boss_dying, sizeof(int), 1, fp );
  3603.     fread( &Boss_dying_sound_playing, sizeof(int), 1, fp );
  3604.     fread( &Boss_hit_this_frame, sizeof(int), 1, fp );
  3605.     fread( &Boss_been_hit, sizeof(int), 1, fp );
  3606.     return 1;
  3607. }
  3608.  
  3609. // -- void show_path_and_other(object *objp )
  3610. // -- {
  3611. // --   int         i;
  3612. // --   ai_static   *aip = &objp->ctype.ai_info;
  3613.  
  3614. // --   for (i=0; i<aip->path_length; i++)
  3615. // --       mprintf((0, "%2i ", Point_segs[aip->hide_index+i].segnum));
  3616. // --   mprintf((0, "[pl: %i cur: st: %i]\n", ConsoleObject->segnum, objp->segnum, aip->hide_segment));
  3617.  
  3618. // -- }
  3619.  
  3620.